gotham/service/
trap.rs

1//! Defines functionality for processing a request and trapping errors and panics in response
2//! generation.
3
4use std::panic::{catch_unwind, AssertUnwindSafe, UnwindSafe};
5
6use futures_util::future::FutureExt;
7use hyper::{Body, Response, StatusCode};
8use log::error;
9
10use crate::handler::{Handler, HandlerError, IntoResponse, NewHandler};
11use crate::state::{request_id, State};
12
13async fn handle<H>(
14    handler: H,
15    state: AssertUnwindSafe<State>,
16) -> Result<(State, Response<Body>), (State, HandlerError)>
17where
18    H: Handler,
19{
20    let AssertUnwindSafe(state) = state;
21    handler.handle(state).await
22}
23
24/// Instantiates a `Handler` from the given `NewHandler`, and invokes it with the request. If a
25/// panic occurs from `NewHandler::new_handler` or `Handler::handle`, it is trapped and will result
26/// in a `500 Internal Server Error` response.
27///
28/// Timing information is recorded and logged, except in the case of a panic where the timer is
29/// moved and cannot be recovered.
30pub async fn call_handler<T>(t: T, state: AssertUnwindSafe<State>) -> anyhow::Result<Response<Body>>
31where
32    T: NewHandler + Send + UnwindSafe,
33{
34    match catch_unwind(move || t.new_handler()) {
35        Ok(handler) => {
36            let unwind_result = AssertUnwindSafe(handle(handler?, state))
37                .catch_unwind()
38                .await;
39            let result = match unwind_result {
40                Ok(result) => result.map(|(_, res)| res),
41                Err(_) => Ok(finalize_panic_response()),
42            };
43            Ok(match result {
44                Ok(res) => res,
45                Err((state, err)) => finalize_error_response(state, err),
46            })
47        }
48        // Error while creating the handler from NewHandler
49        Err(_) => Ok(finalize_panic_response()),
50    }
51}
52
53fn finalize_error_response(state: State, err: HandlerError) -> Response<Body> {
54    error!("[ERROR][{}][Error: {:?}]", request_id(&state), err);
55
56    err.into_response(&state)
57}
58
59fn finalize_panic_response() -> Response<Body> {
60    error!("[PANIC][A panic occurred while invoking the handler]");
61
62    Response::builder()
63        .status(StatusCode::INTERNAL_SERVER_ERROR)
64        .body(Body::default())
65        .unwrap()
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    use futures_util::future;
73    use hyper::{HeaderMap, Method, StatusCode};
74    use std::io;
75    use std::pin::Pin;
76
77    use crate::handler::HandlerFuture;
78    use crate::helpers::http::response::create_empty_response;
79    use crate::state::set_request_id;
80
81    #[test]
82    fn success() {
83        let new_handler = || {
84            Ok(|state| {
85                let res = create_empty_response(&state, StatusCode::ACCEPTED);
86                (state, res)
87            })
88        };
89
90        let mut state = State::new();
91        state.put(HeaderMap::new());
92        state.put(Method::GET);
93        set_request_id(&mut state);
94
95        let r = call_handler(&new_handler, AssertUnwindSafe(state));
96        let response = futures_executor::block_on(r).unwrap();
97        assert_eq!(response.status(), StatusCode::ACCEPTED);
98    }
99
100    #[test]
101    fn async_success_repeat_poll() {
102        let new_handler = || {
103            Ok(|state| {
104                let f = future::lazy(move |_| {
105                    let res = create_empty_response(&state, StatusCode::ACCEPTED);
106                    Ok((state, res))
107                });
108
109                let f = f.map(|v| v);
110                let f = f.map(|v| v);
111                let f = f.map(|v| v);
112
113                f.boxed()
114            })
115        };
116
117        let mut state = State::new();
118        state.put(HeaderMap::new());
119        state.put(Method::GET);
120        set_request_id(&mut state);
121
122        let r = call_handler(&new_handler, AssertUnwindSafe(state));
123        let response = futures_executor::block_on(r).unwrap();
124        assert_eq!(response.status(), StatusCode::ACCEPTED);
125    }
126
127    #[test]
128    fn error() {
129        let new_handler =
130            || Ok(|state| future::err((state, io::Error::last_os_error().into())).boxed());
131
132        let mut state = State::new();
133        state.put(HeaderMap::new());
134        state.put(Method::GET);
135        set_request_id(&mut state);
136
137        let r = call_handler(&new_handler, AssertUnwindSafe(state));
138        let response = futures_executor::block_on(r).unwrap();
139        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
140    }
141
142    #[test]
143    fn panic() {
144        #[allow(clippy::unnecessary_literal_unwrap)] // false positive? or bad description?
145        let new_handler = || {
146            Ok(|_| {
147                let val: Option<Pin<Box<HandlerFuture>>> = None;
148                val.expect("test panic")
149            })
150        };
151
152        let mut state = State::new();
153        state.put(HeaderMap::new());
154        state.put(Method::GET);
155        set_request_id(&mut state);
156
157        let r = call_handler(&new_handler, AssertUnwindSafe(state));
158        let response = futures_executor::block_on(r).unwrap();
159        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
160    }
161
162    #[test]
163    fn async_panic() {
164        let new_handler = || Ok(|_| future::lazy(move |_| panic!("test panic")).boxed());
165
166        let mut state = State::new();
167        state.put(HeaderMap::new());
168        state.put(Method::GET);
169        set_request_id(&mut state);
170
171        let r = call_handler(&new_handler, AssertUnwindSafe(state));
172        let response = futures_executor::block_on(r).unwrap();
173        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
174    }
175
176    #[test]
177    fn async_panic_repeat_poll() {
178        let new_handler = || {
179            Ok(|_| {
180                let f = future::lazy(move |_| panic!("test panic"));
181
182                let f = f.map(|v| v);
183                let f = f.map(|v| v);
184                let f = f.map(|v| v);
185
186                f.boxed()
187            })
188        };
189
190        let mut state = State::new();
191        state.put(HeaderMap::new());
192        state.put(Method::GET);
193        set_request_id(&mut state);
194
195        let r = call_handler(&new_handler, AssertUnwindSafe(state));
196        let response = futures_executor::block_on(r).unwrap();
197        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
198    }
199
200    #[test]
201    fn new_handler_panic() {
202        struct PanicNewHandler;
203        impl NewHandler for PanicNewHandler {
204            type Instance = Self;
205
206            fn new_handler(&self) -> anyhow::Result<Self::Instance> {
207                panic!("Pannicked creating a new handler");
208            }
209        }
210
211        impl Handler for PanicNewHandler {
212            fn handle(self, _state: State) -> Pin<Box<HandlerFuture>> {
213                unreachable!();
214            }
215        }
216
217        let mut state = State::new();
218        state.put(HeaderMap::new());
219        state.put(Method::GET);
220        set_request_id(&mut state);
221
222        let new_handler = PanicNewHandler {};
223        let r = call_handler(new_handler, AssertUnwindSafe(state));
224        let response = futures_executor::block_on(r).unwrap();
225        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
226    }
227}