1 //! Rejections
2 //!
3 //! Part of the power of the [`Filter`](../trait.Filter.html) system is being able to
4 //! reject a request from a filter chain. This allows for filters to be
5 //! combined with `or`, so that if one side of the chain finds that a request
6 //! doesn't fulfill its requirements, the other side can try to process
7 //! the request.
8 //!
9 //! Many of the built-in [`filters`](../filters) will automatically reject
10 //! the request with an appropriate rejection. However, you can also build
11 //! new custom [`Filter`](../trait.Filter.html)s and still want other routes to be
12 //! matchable in the case a predicate doesn't hold.
13 //!
14 //! # Example
15 //!
16 //! ```
17 //! use warp::Filter;
18 //!
19 //! // Filter on `/:id`, but reject with 404 if the `id` is `0`.
20 //! let route = warp::path::param()
21 //!     .and_then(|id: u32| {
22 //!         if id == 0 {
23 //!             Err(warp::reject::not_found())
24 //!         } else {
25 //!             Ok("something since id is valid")
26 //!         }
27 //!     });
28 //! ```
29 
30 use std::error::Error as StdError;
31 use std::fmt;
32 
33 use http::{
34     self,
35     header::{HeaderValue, CONTENT_TYPE},
36     StatusCode,
37 };
38 use hyper::Body;
39 use serde;
40 use serde_json;
41 
42 use never::Never;
43 
44 pub(crate) use self::sealed::{CombineRejection, Reject};
45 
46 //TODO(v0.2): This should just be `type Cause = StdError + Send + Sync + 'static`,
47 //and not include the `Box`.
48 #[doc(hidden)]
49 pub type Cause = Box<dyn StdError + Send + Sync>;
50 
51 #[doc(hidden)]
52 #[deprecated(
53     note = "this will be changed to return a NotFound rejection, use warp::reject::custom for custom bad requests"
54 )]
55 #[allow(deprecated)]
56 #[inline]
reject() -> Rejection57 pub fn reject() -> Rejection {
58     bad_request()
59 }
60 
61 #[doc(hidden)]
62 #[deprecated(note = "use warp::reject::custom and Filter::recover to send a 401 error")]
bad_request() -> Rejection63 pub fn bad_request() -> Rejection {
64     Rejection::known_status(StatusCode::BAD_REQUEST)
65 }
66 
67 #[doc(hidden)]
68 #[deprecated(note = "use warp::reject::custom and Filter::recover to send a 403 error")]
forbidden() -> Rejection69 pub fn forbidden() -> Rejection {
70     Rejection::known_status(StatusCode::FORBIDDEN)
71 }
72 
73 /// Rejects a request with `404 Not Found`.
74 #[inline]
not_found() -> Rejection75 pub fn not_found() -> Rejection {
76     Rejection {
77         reason: Reason::NotFound,
78     }
79 }
80 
81 // 400 Bad Request
82 #[inline]
invalid_query() -> Rejection83 pub(crate) fn invalid_query() -> Rejection {
84     known(InvalidQuery(()))
85 }
86 
87 // 400 Bad Request
88 #[inline]
missing_header(name: &'static str) -> Rejection89 pub(crate) fn missing_header(name: &'static str) -> Rejection {
90     known(MissingHeader(name))
91 }
92 
93 // 400 Bad Request
94 #[inline]
invalid_header(name: &'static str) -> Rejection95 pub(crate) fn invalid_header(name: &'static str) -> Rejection {
96     known(InvalidHeader(name))
97 }
98 
99 // 400 Bad Request
100 #[inline]
missing_cookie(name: &'static str) -> Rejection101 pub(crate) fn missing_cookie(name: &'static str) -> Rejection {
102     known(MissingCookie(name))
103 }
104 
105 // 405 Method Not Allowed
106 #[inline]
method_not_allowed() -> Rejection107 pub(crate) fn method_not_allowed() -> Rejection {
108     known(MethodNotAllowed(()))
109 }
110 
111 // 411 Length Required
112 #[inline]
length_required() -> Rejection113 pub(crate) fn length_required() -> Rejection {
114     known(LengthRequired(()))
115 }
116 
117 // 413 Payload Too Large
118 #[inline]
payload_too_large() -> Rejection119 pub(crate) fn payload_too_large() -> Rejection {
120     known(PayloadTooLarge(()))
121 }
122 
123 // 415 Unsupported Media Type
124 //
125 // Used by the body filters if the request payload content-type doesn't match
126 // what can be deserialized.
127 #[inline]
unsupported_media_type() -> Rejection128 pub(crate) fn unsupported_media_type() -> Rejection {
129     known(UnsupportedMediaType(()))
130 }
131 
132 #[doc(hidden)]
133 #[deprecated(note = "use warp::reject::custom and Filter::recover to send a 500 error")]
server_error() -> Rejection134 pub fn server_error() -> Rejection {
135     Rejection::known_status(StatusCode::INTERNAL_SERVER_ERROR)
136 }
137 
138 /// Rejects a request with a custom cause.
139 ///
140 /// A [`recover`][] filter should convert this `Rejection` into a `Reply`,
141 /// or else this will be returned as a `500 Internal Server Error`.
142 ///
143 /// [`recover`]: ../../trait.Filter.html#method.recover
custom(err: impl Into<Cause>) -> Rejection144 pub fn custom(err: impl Into<Cause>) -> Rejection {
145     Rejection::custom(err.into())
146 }
147 
known(err: impl Into<Cause>) -> Rejection148 pub(crate) fn known(err: impl Into<Cause>) -> Rejection {
149     Rejection::known(err.into())
150 }
151 
152 /// Rejection of a request by a [`Filter`](::Filter).
153 ///
154 /// See the [`reject`](index.html) documentation for more.
155 pub struct Rejection {
156     reason: Reason,
157 }
158 
159 enum Reason {
160     NotFound,
161     Other(Box<Rejections>),
162 }
163 
164 enum Rejections {
165     //TODO(v0.2): For 0.1, this needs to hold a Box<StdError>, in order to support
166     //cause() returning a `&Box<StdError>`. With 0.2, this should no longer need
167     //to be boxed.
168     Known(Cause),
169     KnownStatus(StatusCode),
170     With(Rejection, Cause),
171     Custom(Cause),
172     Combined(Box<Rejections>, Box<Rejections>),
173 }
174 
175 impl Rejection {
known(other: Cause) -> Self176     fn known(other: Cause) -> Self {
177         Rejection {
178             reason: Reason::Other(Box::new(Rejections::Known(other))),
179         }
180     }
181 
known_status(status: StatusCode) -> Self182     fn known_status(status: StatusCode) -> Self {
183         Rejection {
184             reason: Reason::Other(Box::new(Rejections::KnownStatus(status))),
185         }
186     }
187 
custom(other: Cause) -> Self188     fn custom(other: Cause) -> Self {
189         Rejection {
190             reason: Reason::Other(Box::new(Rejections::Custom(other))),
191         }
192     }
193 
194     /// Searches this `Rejection` for a specific cause.
195     ///
196     /// A `Rejection` will accumulate causes over a `Filter` chain. This method
197     /// can search through them and return the first cause of this type.
198     ///
199     /// # Example
200     ///
201     /// ```
202     /// use std::io;
203     ///
204     /// let err = io::Error::new(
205     ///     io::ErrorKind::Other,
206     ///     "could be any std::error::Error"
207     /// );
208     /// let reject = warp::reject::custom(err);
209     ///
210     /// if let Some(cause) = reject.find_cause::<io::Error>() {
211     ///    println!("found the io::Error: {}", cause);
212     /// }
213     /// ```
find_cause<T: StdError + 'static>(&self) -> Option<&T>214     pub fn find_cause<T: StdError + 'static>(&self) -> Option<&T> {
215         if let Reason::Other(ref rejections) = self.reason {
216             return rejections.find_cause();
217         }
218         None
219     }
220 
221     /// Returns true if this Rejection was made via `warp::reject::not_found`.
222     ///
223     /// # Example
224     ///
225     /// ```
226     /// let rejection = warp::reject::not_found();
227     ///
228     /// assert!(rejection.is_not_found());
229     /// ```
is_not_found(&self) -> bool230     pub fn is_not_found(&self) -> bool {
231         if let Reason::NotFound = self.reason {
232             true
233         } else {
234             false
235         }
236     }
237 
238     #[doc(hidden)]
status(&self) -> StatusCode239     pub fn status(&self) -> StatusCode {
240         Reject::status(self)
241     }
242 
243     #[doc(hidden)]
244     #[deprecated(note = "Custom rejections should use `warp::reject::custom()`.")]
with<E>(self, err: E) -> Self where E: Into<Cause>,245     pub fn with<E>(self, err: E) -> Self
246     where
247         E: Into<Cause>,
248     {
249         let cause = err.into();
250 
251         Self {
252             reason: Reason::Other(Box::new(Rejections::With(self, cause))),
253         }
254     }
255 
256     #[doc(hidden)]
257     #[deprecated(note = "Use warp::reply::json and warp::reply::with_status instead.")]
json(&self) -> ::reply::Response258     pub fn json(&self) -> ::reply::Response {
259         let code = self.status();
260         let mut res = http::Response::default();
261         *res.status_mut() = code;
262 
263         res.headers_mut()
264             .insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
265 
266         *res.body_mut() = match serde_json::to_string(&self) {
267             Ok(body) => Body::from(body),
268             Err(_) => Body::from("{}"),
269         };
270 
271         res
272     }
273 
274     /// Returns an optional error cause for this rejection.
275     ///
276     /// If this `Rejection` is actuall a combination of rejections, then the
277     /// returned cause is determined by an internal ranking system. If you'd
278     /// rather handle different causes with different priorities, use
279     /// `find_cause`.
280     ///
281     /// # Note
282     ///
283     /// The return type will change from `&Box<Error>` to `&Error` in v0.2.
284     /// This method isn't marked deprecated, however, since most people aren't
285     /// actually using the `Box` part, and so a deprecation warning would just
286     /// annoy people who didn't need to make any changes.
cause(&self) -> Option<&Cause>287     pub fn cause(&self) -> Option<&Cause> {
288         if let Reason::Other(ref err) = self.reason {
289             return err.cause();
290         }
291         None
292     }
293 
294     #[doc(hidden)]
295     #[deprecated(note = "into_cause can no longer be provided")]
into_cause<T>(self) -> Result<Box<T>, Self> where T: StdError + Send + Sync + 'static,296     pub fn into_cause<T>(self) -> Result<Box<T>, Self>
297     where
298         T: StdError + Send + Sync + 'static,
299     {
300         Err(self)
301     }
302 }
303 
304 impl From<Never> for Rejection {
305     #[inline]
from(never: Never) -> Rejection306     fn from(never: Never) -> Rejection {
307         match never {}
308     }
309 }
310 
311 impl Reject for Never {
status(&self) -> StatusCode312     fn status(&self) -> StatusCode {
313         match *self {}
314     }
315 
into_response(&self) -> ::reply::Response316     fn into_response(&self) -> ::reply::Response {
317         match *self {}
318     }
319 
cause(&self) -> Option<&Cause>320     fn cause(&self) -> Option<&Cause> {
321         None
322     }
323 }
324 
325 impl Reject for Rejection {
status(&self) -> StatusCode326     fn status(&self) -> StatusCode {
327         match self.reason {
328             Reason::NotFound => StatusCode::NOT_FOUND,
329             Reason::Other(ref other) => other.status(),
330         }
331     }
332 
into_response(&self) -> ::reply::Response333     fn into_response(&self) -> ::reply::Response {
334         match self.reason {
335             Reason::NotFound => {
336                 let mut res = http::Response::default();
337                 *res.status_mut() = StatusCode::NOT_FOUND;
338                 res
339             }
340             Reason::Other(ref other) => other.into_response(),
341         }
342     }
343 
cause(&self) -> Option<&Cause>344     fn cause(&self) -> Option<&Cause> {
345         Rejection::cause(&self)
346     }
347 }
348 
349 impl fmt::Debug for Rejection {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result350     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
351         f.debug_tuple("Rejection").field(&self.reason).finish()
352     }
353 }
354 
355 impl fmt::Debug for Reason {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result356     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
357         match *self {
358             Reason::NotFound => f.write_str("NotFound"),
359             Reason::Other(ref other) => fmt::Debug::fmt(other, f),
360         }
361     }
362 }
363 
364 #[doc(hidden)]
365 #[deprecated(note = "Use warp::reply::json and warp::reply::with_status instead.")]
366 impl serde::Serialize for Rejection {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer,367     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
368     where
369         S: serde::Serializer,
370     {
371         use serde::ser::SerializeMap;
372 
373         let mut map = serializer.serialize_map(None)?;
374         let err = match self.cause() {
375             Some(err) => err,
376             None => return map.end(),
377         };
378 
379         map.serialize_key("description")
380             .and_then(|_| map.serialize_value(err.description()))?;
381         map.serialize_key("message")
382             .and_then(|_| map.serialize_value(&err.to_string()))?;
383         map.end()
384     }
385 }
386 
387 // ===== Rejections =====
388 
389 impl Rejections {
status(&self) -> StatusCode390     fn status(&self) -> StatusCode {
391         match *self {
392             Rejections::Known(ref e) => {
393                 if e.is::<MethodNotAllowed>() {
394                     StatusCode::METHOD_NOT_ALLOWED
395                 } else if e.is::<InvalidHeader>() {
396                     StatusCode::BAD_REQUEST
397                 } else if e.is::<MissingHeader>() {
398                     StatusCode::BAD_REQUEST
399                 } else if e.is::<MissingCookie>() {
400                     StatusCode::BAD_REQUEST
401                 } else if e.is::<InvalidQuery>() {
402                     StatusCode::BAD_REQUEST
403                 } else if e.is::<LengthRequired>() {
404                     StatusCode::LENGTH_REQUIRED
405                 } else if e.is::<PayloadTooLarge>() {
406                     StatusCode::PAYLOAD_TOO_LARGE
407                 } else if e.is::<UnsupportedMediaType>() {
408                     StatusCode::UNSUPPORTED_MEDIA_TYPE
409                 } else if e.is::<::body::BodyReadError>() {
410                     StatusCode::BAD_REQUEST
411                 } else if e.is::<::body::BodyDeserializeError>() {
412                     StatusCode::BAD_REQUEST
413                 } else if e.is::<::cors::CorsForbidden>() {
414                     StatusCode::FORBIDDEN
415                 } else if e.is::<::ext::MissingExtension>() {
416                     StatusCode::INTERNAL_SERVER_ERROR
417                 } else if e.is::<::reply::ReplyHttpError>() {
418                     StatusCode::INTERNAL_SERVER_ERROR
419                 } else if e.is::<::reply::ReplyJsonError>() {
420                     StatusCode::INTERNAL_SERVER_ERROR
421                 } else if e.is::<::body::BodyConsumedMultipleTimes>() {
422                     StatusCode::INTERNAL_SERVER_ERROR
423                 } else if e.is::<::fs::FsNeedsTokioThreadpool>() {
424                     StatusCode::INTERNAL_SERVER_ERROR
425                 } else {
426                     unreachable!("unexpected 'Known' rejection: {:?}", e);
427                 }
428             }
429             Rejections::KnownStatus(status) => status,
430             Rejections::With(ref rej, _) => rej.status(),
431             Rejections::Custom(..) => StatusCode::INTERNAL_SERVER_ERROR,
432             Rejections::Combined(ref a, ref b) => preferred(a, b).status(),
433         }
434     }
435 
into_response(&self) -> ::reply::Response436     fn into_response(&self) -> ::reply::Response {
437         match *self {
438             Rejections::Known(ref e) => {
439                 let mut res = http::Response::new(Body::from(e.to_string()));
440                 *res.status_mut() = self.status();
441                 res.headers_mut().insert(
442                     CONTENT_TYPE,
443                     HeaderValue::from_static("text/plain; charset=utf-8"),
444                 );
445                 res
446             }
447             Rejections::KnownStatus(ref s) => {
448                 use reply::Reply;
449                 s.into_response()
450             }
451             Rejections::With(ref rej, ref e) => {
452                 let mut res = rej.into_response();
453 
454                 let bytes = e.to_string();
455                 res.headers_mut().insert(
456                     CONTENT_TYPE,
457                     HeaderValue::from_static("text/plain; charset=utf-8"),
458                 );
459                 *res.body_mut() = Body::from(bytes);
460 
461                 res
462             }
463             Rejections::Custom(ref e) => {
464                 error!(
465                     "unhandled custom rejection, returning 500 response: {:?}",
466                     e
467                 );
468                 let body = format!("Unhandled rejection: {}", e);
469                 let mut res = http::Response::new(Body::from(body));
470                 *res.status_mut() = self.status();
471                 res.headers_mut().insert(
472                     CONTENT_TYPE,
473                     HeaderValue::from_static("text/plain; charset=utf-8"),
474                 );
475                 res
476             }
477             Rejections::Combined(ref a, ref b) => preferred(a, b).into_response(),
478         }
479     }
480 
cause(&self) -> Option<&Cause>481     fn cause(&self) -> Option<&Cause> {
482         match *self {
483             Rejections::Known(ref e) => Some(e),
484             Rejections::KnownStatus(_) => None,
485             Rejections::With(_, ref e) => Some(e),
486             Rejections::Custom(ref e) => Some(e),
487             Rejections::Combined(ref a, ref b) => preferred(a, b).cause(),
488         }
489     }
490 
find_cause<T: StdError + 'static>(&self) -> Option<&T>491     pub fn find_cause<T: StdError + 'static>(&self) -> Option<&T> {
492         match *self {
493             Rejections::Known(ref e) => e.downcast_ref(),
494             Rejections::KnownStatus(_) => None,
495             Rejections::With(_, ref e) => e.downcast_ref(),
496             Rejections::Custom(ref e) => e.downcast_ref(),
497             Rejections::Combined(ref a, ref b) => a.find_cause().or_else(|| b.find_cause()),
498         }
499     }
500 }
501 
preferred<'a>(a: &'a Rejections, b: &'a Rejections) -> &'a Rejections502 fn preferred<'a>(a: &'a Rejections, b: &'a Rejections) -> &'a Rejections {
503     // Compare status codes, with this priority:
504     // - NOT_FOUND is lowest
505     // - METHOD_NOT_ALLOWED is second
506     // - if one status code is greater than the other
507     // - otherwise, prefer A...
508     match (a.status(), b.status()) {
509         (_, StatusCode::NOT_FOUND) => a,
510         (StatusCode::NOT_FOUND, _) => b,
511         (_, StatusCode::METHOD_NOT_ALLOWED) => a,
512         (StatusCode::METHOD_NOT_ALLOWED, _) => b,
513         (sa, sb) if sa < sb => b,
514         _ => a,
515     }
516 }
517 
518 impl fmt::Debug for Rejections {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result519     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520         match *self {
521             Rejections::Known(ref e) => fmt::Debug::fmt(e, f),
522             Rejections::KnownStatus(ref s) => f.debug_tuple("Status").field(s).finish(),
523             Rejections::With(ref rej, ref e) => f.debug_tuple("With").field(rej).field(e).finish(),
524             Rejections::Custom(ref e) => f.debug_tuple("Custom").field(e).finish(),
525             Rejections::Combined(ref a, ref b) => {
526                 f.debug_tuple("Combined").field(a).field(b).finish()
527             }
528         }
529     }
530 }
531 
532 /// Invalid query
533 #[derive(Debug)]
534 pub struct InvalidQuery(());
535 
536 impl ::std::fmt::Display for InvalidQuery {
fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result537     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
538         f.write_str("Invalid query string")
539     }
540 }
541 
542 impl StdError for InvalidQuery {
description(&self) -> &str543     fn description(&self) -> &str {
544         "Invalid query string"
545     }
546 }
547 
548 /// HTTP method not allowed
549 #[derive(Debug)]
550 pub struct MethodNotAllowed(());
551 
552 impl fmt::Display for MethodNotAllowed {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result553     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554         f.write_str("HTTP method not allowed")
555     }
556 }
557 
558 impl StdError for MethodNotAllowed {
description(&self) -> &str559     fn description(&self) -> &str {
560         "HTTP method not allowed"
561     }
562 }
563 
564 /// A content-length header is required
565 #[derive(Debug)]
566 pub struct LengthRequired(());
567 
568 impl fmt::Display for LengthRequired {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result569     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
570         f.write_str("A content-length header is required")
571     }
572 }
573 
574 impl StdError for LengthRequired {
description(&self) -> &str575     fn description(&self) -> &str {
576         "A content-length header is required"
577     }
578 }
579 
580 /// The request payload is too large
581 #[derive(Debug)]
582 pub struct PayloadTooLarge(());
583 
584 impl fmt::Display for PayloadTooLarge {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result585     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
586         f.write_str("The request payload is too large")
587     }
588 }
589 
590 impl StdError for PayloadTooLarge {
description(&self) -> &str591     fn description(&self) -> &str {
592         "The request payload is too large"
593     }
594 }
595 
596 /// The request's content-type is not supported
597 #[derive(Debug)]
598 pub struct UnsupportedMediaType(());
599 
600 impl fmt::Display for UnsupportedMediaType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result601     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
602         f.write_str("The request's content-type is not supported")
603     }
604 }
605 
606 impl StdError for UnsupportedMediaType {
description(&self) -> &str607     fn description(&self) -> &str {
608         "The request's content-type is not supported"
609     }
610 }
611 
612 /// Missing request header
613 #[derive(Debug)]
614 pub struct MissingHeader(&'static str);
615 
616 impl ::std::fmt::Display for MissingHeader {
fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result617     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
618         write!(f, "Missing request header '{}'", self.0)
619     }
620 }
621 
622 impl StdError for MissingHeader {
description(&self) -> &str623     fn description(&self) -> &str {
624         "Missing request header"
625     }
626 }
627 
628 /// Invalid request header
629 #[derive(Debug)]
630 pub struct InvalidHeader(&'static str);
631 
632 impl ::std::fmt::Display for InvalidHeader {
fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result633     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
634         write!(f, "Invalid request header '{}'", self.0)
635     }
636 }
637 
638 impl StdError for InvalidHeader {
description(&self) -> &str639     fn description(&self) -> &str {
640         "Invalid request header"
641     }
642 }
643 
644 
645 /// Missing cookie
646 #[derive(Debug)]
647 pub struct MissingCookie(&'static str);
648 
649 impl ::std::fmt::Display for MissingCookie {
fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result650     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
651         write!(f, "Missing request cookie '{}'", self.0)
652     }
653 }
654 
655 impl StdError for MissingCookie {
description(&self) -> &str656     fn description(&self) -> &str {
657         "Missing request cookie"
658     }
659 }
660 
661 trait Typed: StdError + 'static {
type_id(&self) -> ::std::any::TypeId662     fn type_id(&self) -> ::std::any::TypeId;
663 }
664 
665 mod sealed {
666     use super::{Cause, Reason, Rejection, Rejections};
667     use http::StatusCode;
668     use never::Never;
669     use std::fmt;
670 
671     pub trait Reject: fmt::Debug + Send + Sync {
status(&self) -> StatusCode672         fn status(&self) -> StatusCode;
into_response(&self) -> ::reply::Response673         fn into_response(&self) -> ::reply::Response;
cause(&self) -> Option<&Cause>674         fn cause(&self) -> Option<&Cause> {
675             None
676         }
677     }
678 
_assert_object_safe()679     fn _assert_object_safe() {
680         fn _assert(_: &dyn Reject) {}
681     }
682 
683     pub trait CombineRejection<E>: Send + Sized {
684         type Rejection: Reject + From<Self> + From<E> + Into<Rejection>;
685 
combine(self, other: E) -> Self::Rejection686         fn combine(self, other: E) -> Self::Rejection;
687     }
688 
689     impl CombineRejection<Rejection> for Rejection {
690         type Rejection = Rejection;
691 
combine(self, other: Rejection) -> Self::Rejection692         fn combine(self, other: Rejection) -> Self::Rejection {
693             let reason = match (self.reason, other.reason) {
694                 (Reason::Other(left), Reason::Other(right)) => {
695                     Reason::Other(Box::new(Rejections::Combined(left, right)))
696                 }
697                 (Reason::Other(other), Reason::NotFound)
698                 | (Reason::NotFound, Reason::Other(other)) => {
699                     // ignore the NotFound
700                     Reason::Other(other)
701                 }
702                 (Reason::NotFound, Reason::NotFound) => Reason::NotFound,
703             };
704 
705             Rejection { reason }
706         }
707     }
708 
709     impl CombineRejection<Never> for Rejection {
710         type Rejection = Rejection;
711 
combine(self, other: Never) -> Self::Rejection712         fn combine(self, other: Never) -> Self::Rejection {
713             match other {}
714         }
715     }
716 
717     impl CombineRejection<Rejection> for Never {
718         type Rejection = Rejection;
719 
combine(self, _: Rejection) -> Self::Rejection720         fn combine(self, _: Rejection) -> Self::Rejection {
721             match self {}
722         }
723     }
724 
725     impl CombineRejection<Never> for Never {
726         type Rejection = Never;
727 
combine(self, _: Never) -> Self::Rejection728         fn combine(self, _: Never) -> Self::Rejection {
729             match self {}
730         }
731     }
732 }
733 
734 #[cfg(test)]
735 mod tests {
736     use http::header::CONTENT_TYPE;
737 
738     use super::*;
739     use http::StatusCode;
740 
741     #[allow(deprecated)]
742     #[test]
rejection_status()743     fn rejection_status() {
744         assert_eq!(bad_request().status(), StatusCode::BAD_REQUEST);
745         assert_eq!(forbidden().status(), StatusCode::FORBIDDEN);
746         assert_eq!(not_found().status(), StatusCode::NOT_FOUND);
747         assert_eq!(
748             method_not_allowed().status(),
749             StatusCode::METHOD_NOT_ALLOWED
750         );
751         assert_eq!(length_required().status(), StatusCode::LENGTH_REQUIRED);
752         assert_eq!(payload_too_large().status(), StatusCode::PAYLOAD_TOO_LARGE);
753         assert_eq!(
754             unsupported_media_type().status(),
755             StatusCode::UNSUPPORTED_MEDIA_TYPE
756         );
757         assert_eq!(server_error().status(), StatusCode::INTERNAL_SERVER_ERROR);
758         assert_eq!(custom("boom").status(), StatusCode::INTERNAL_SERVER_ERROR);
759     }
760 
761     #[allow(deprecated)]
762     #[test]
combine_rejections()763     fn combine_rejections() {
764         let left = bad_request().with("left");
765         let right = server_error().with("right");
766         let reject = left.combine(right);
767 
768         assert_eq!(reject.status(), StatusCode::INTERNAL_SERVER_ERROR);
769         assert_eq!(reject.cause().unwrap().to_string(), "right");
770     }
771 
772     #[allow(deprecated)]
773     #[test]
combine_rejection_causes_with_some_left_and_none_server_error()774     fn combine_rejection_causes_with_some_left_and_none_server_error() {
775         let left = bad_request().with("left");
776         let right = server_error();
777         let reject = left.combine(right);
778 
779         assert_eq!(reject.status(), StatusCode::INTERNAL_SERVER_ERROR);
780         assert!(reject.cause().is_none());
781     }
782 
783     #[allow(deprecated)]
784     #[test]
combine_rejection_causes_with_some_left_and_none_right()785     fn combine_rejection_causes_with_some_left_and_none_right() {
786         let left = bad_request().with("left");
787         let right = bad_request();
788         let reject = left.combine(right);
789 
790         assert_eq!(reject.status(), StatusCode::BAD_REQUEST);
791         assert_eq!(reject.cause().unwrap().to_string(), "left");
792     }
793 
794     #[allow(deprecated)]
795     #[test]
combine_rejection_causes_with_none_left_and_some_right()796     fn combine_rejection_causes_with_none_left_and_some_right() {
797         let left = bad_request();
798         let right = server_error().with("right");
799         let reject = left.combine(right);
800 
801         assert_eq!(reject.status(), StatusCode::INTERNAL_SERVER_ERROR);
802         assert_eq!(reject.cause().unwrap().to_string(), "right");
803     }
804 
805     #[allow(deprecated)]
806     #[test]
unhandled_customs()807     fn unhandled_customs() {
808         let reject = bad_request().combine(custom("right"));
809 
810         let resp = reject.into_response();
811         assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
812         assert_eq!(response_body_string(resp), "Unhandled rejection: right");
813 
814         // There's no real way to determine which is worse, since both are a 500,
815         // so pick the first one.
816         let reject = server_error().combine(custom("right"));
817 
818         let resp = reject.into_response();
819         assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
820         assert_eq!(response_body_string(resp), "");
821 
822         // With many rejections, custom still is top priority.
823         let reject = bad_request()
824             .combine(bad_request())
825             .combine(not_found())
826             .combine(custom("right"))
827             .combine(bad_request());
828 
829         let resp = reject.into_response();
830         assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
831         assert_eq!(response_body_string(resp), "Unhandled rejection: right");
832     }
833 
834     #[test]
into_response_with_none_cause()835     fn into_response_with_none_cause() {
836         let resp = not_found().into_response();
837         assert_eq!(404, resp.status());
838         assert!(resp.headers().get(CONTENT_TYPE).is_none());
839         assert_eq!("", response_body_string(resp))
840     }
841 
842     #[allow(deprecated)]
843     #[test]
into_response_with_some_cause()844     fn into_response_with_some_cause() {
845         let resp = server_error().with("boom").into_response();
846         assert_eq!(500, resp.status());
847         assert_eq!(
848             "text/plain; charset=utf-8",
849             resp.headers().get(CONTENT_TYPE).unwrap()
850         );
851         assert_eq!("boom", response_body_string(resp))
852     }
853 
854     #[allow(deprecated)]
855     #[test]
into_json_with_none_cause()856     fn into_json_with_none_cause() {
857         let resp = not_found().json();
858         assert_eq!(404, resp.status());
859         assert_eq!(
860             "application/json",
861             resp.headers().get(CONTENT_TYPE).unwrap()
862         );
863         assert_eq!("{}", response_body_string(resp))
864     }
865 
866     #[allow(deprecated)]
867     #[test]
into_json_with_some_cause()868     fn into_json_with_some_cause() {
869         let resp = bad_request().with("boom").json();
870         assert_eq!(400, resp.status());
871         assert_eq!(
872             "application/json",
873             resp.headers().get(CONTENT_TYPE).unwrap()
874         );
875         let expected = "{\"description\":\"boom\",\"message\":\"boom\"}";
876         assert_eq!(expected, response_body_string(resp))
877     }
878 
response_body_string(resp: ::reply::Response) -> String879     fn response_body_string(resp: ::reply::Response) -> String {
880         use futures::{Async, Future, Stream};
881 
882         let (_, body) = resp.into_parts();
883         match body.concat2().poll() {
884             Ok(Async::Ready(chunk)) => String::from_utf8_lossy(&chunk).to_string(),
885             err => unreachable!("{:?}", err),
886         }
887     }
888 
889     #[test]
890     #[allow(deprecated)]
into_cause()891     fn into_cause() {
892         use std::io;
893 
894         let reject = bad_request().with(io::Error::new(io::ErrorKind::Other, "boom"));
895 
896         reject.into_cause::<io::Error>().unwrap_err();
897     }
898 
899     #[allow(deprecated)]
900     #[test]
find_cause()901     fn find_cause() {
902         use std::io;
903 
904         let rej = bad_request().with(io::Error::new(io::ErrorKind::Other, "boom"));
905 
906         assert_eq!(rej.find_cause::<io::Error>().unwrap().to_string(), "boom");
907 
908         let rej = bad_request()
909             .with(io::Error::new(io::ErrorKind::Other, "boom"))
910             .combine(method_not_allowed());
911 
912         assert_eq!(rej.find_cause::<io::Error>().unwrap().to_string(), "boom");
913         assert!(
914             rej.find_cause::<MethodNotAllowed>().is_some(),
915             "MethodNotAllowed"
916         );
917     }
918 
919     #[test]
size_of_rejection()920     fn size_of_rejection() {
921         assert_eq!(
922             ::std::mem::size_of::<Rejection>(),
923             ::std::mem::size_of::<usize>(),
924         );
925     }
926 }
927