tower_web/error/
catch.rs

1use error::Error;
2use serde_json;
3use response::Serializer;
4use util::BufStream;
5
6use futures::{future, Future, IntoFuture};
7use http;
8
9use std::sync::Arc;
10
11/// Handles an error generated by a resource method.
12///
13/// In the event that an error occurs while processing an HTTP request,
14/// implementations of `Catch` have the opportunity of gracefully handling the
15/// error. This usually means responding with a friendly error response.
16///
17/// For example, if the error is generated due to no routes matching the HTTP
18/// request, then a 404 response should be returned. If a route was matched, but
19/// the arguments were not satisfied, then a 400 response should be returned.
20pub trait Catch: Clone {
21
22    /// The HTTP response body type
23    type Body: BufStream;
24
25    /// Future yielding the HTTP response.
26    type Future: Future<Item = http::Response<Self::Body>, Error = Error>;
27
28    /// Handles an error.
29    ///
30    /// Implementations of `Catch` generate a friendly HTTP error response based
31    /// on the provideed `request` and `error`.
32    ///
33    /// See module level documentation for more details.
34    fn catch(&mut self, request: &http::Request<()>, error: Error) -> Self::Future;
35}
36
37/// Convert `Self` into a value that implements `Catch`.
38///
39/// By implementing `IntoCatch` for a type, you define how it will be converted
40/// into a `Catch` instance.
41///
42/// The `Serializer` configured for the service is provided. This allows the
43/// `Catch` instance to be able to serialize responses.
44pub trait IntoCatch<S> {
45    /// The `Catch` value being converted to
46    type Catch: Catch;
47
48    /// Convert `self` into a `Catch` value.
49    fn into_catch(self) -> Self::Catch;
50}
51
52/// The default `Catch` instance used when none is specified.
53///
54/// This implementation provides basic error handling by generating simple
55/// HTTP responses based on the error type.
56#[derive(Debug, Clone)]
57pub struct DefaultCatch {
58    _p: (),
59}
60
61/// Provides a `Catch` implementation using the provided `Fn` instance.
62#[derive(Debug)]
63pub struct FnCatch<F>(Arc<F>);
64
65// ===== impl DefaultCatch =====
66
67impl DefaultCatch {
68    /// Create a new `DefaultCatch` instance.
69    pub fn new() -> DefaultCatch {
70        DefaultCatch {
71            _p: (),
72        }
73    }
74}
75
76impl<S> IntoCatch<S> for DefaultCatch
77where S: Serializer,
78{
79    type Catch = DefaultCatch;
80
81    fn into_catch(self) -> Self::Catch {
82        self
83    }
84}
85
86/// Implements `Catch` by returning an HTTP response with the appropriate status
87/// code.
88///
89/// The HTTP response will include a basic error message in the response body.
90impl Catch for DefaultCatch {
91    type Body = String;
92    type Future = future::FutureResult<http::Response<Self::Body>, Error>;
93
94    fn catch(&mut self, _request: &http::Request<()>, error: Error) -> Self::Future {
95        let status = error.status_code().as_u16();
96        let problem = serde_json::to_string(&error)
97            .unwrap_or_else(|_| {
98                serde_json::to_string(&Error::from(http::status::StatusCode::INTERNAL_SERVER_ERROR))
99                    .expect("Error serializing a blank error to JSON")
100            });
101
102        // TODO: Improve the default errors
103        let response = http::response::Builder::new()
104            .status(status)
105            .header("content-type", "application/problem+json")
106            .body(problem)
107            .unwrap();
108
109        future::ok(response)
110    }
111}
112
113// ===== impl FnCatch =====
114
115impl<F, R, Body, S> IntoCatch<S> for F
116where F: Fn(&http::Request<()>, Error) -> R,
117      R: IntoFuture<Item = http::Response<Body>, Error = Error>,
118      Body: BufStream,
119{
120    type Catch = FnCatch<F>;
121
122    fn into_catch(self) -> Self::Catch {
123        FnCatch(Arc::new(self))
124    }
125}
126
127impl<F, R, Body> Catch for FnCatch<F>
128where F: Fn(&http::Request<()>, Error) -> R,
129      R: IntoFuture<Item = http::Response<Body>, Error = Error>,
130      Body: BufStream,
131{
132    type Body = Body;
133    type Future = R::Future;
134
135    fn catch(&mut self, request: &http::Request<()>, error: Error) -> Self::Future {
136        self.0(request, error).into_future()
137    }
138}
139
140impl<F> Clone for FnCatch<F> {
141    fn clone(&self) -> FnCatch<F> {
142        FnCatch(self.0.clone())
143    }
144}