Skip to content

Commit a081bfe

Browse files
committed
Add middleware for catching panics
1 parent 279e0f0 commit a081bfe

6 files changed

Lines changed: 101 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ members = [
6060
"tide-log",
6161
"tide-querystring",
6262
"tide-slog",
63+
"tide-panic",
6364
]
6465

6566
[patch.crates-io]

tide-panic/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "tide-panic"
3+
version = "0.1.0"
4+
edition = "2018"
5+
authors = ["Tide Developers"]
6+
description = "Advanced panic support for Tide"
7+
documentation = "https://docs.rs/tide-panic"
8+
license = "MIT OR Apache-2.0"
9+
repository = "https://github.com/rustasync/tide"
10+
11+
[dependencies]
12+
futures-preview = "0.3.0-alpha.16"
13+
http = "0.1"
14+
http-service = "0.2.0"
15+
tide-core = { path = "../tide-core" }

tide-panic/LICENSE-APACHE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-APACHE

tide-panic/LICENSE-MIT

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-MIT

tide-panic/src/catch_unwind.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use futures::future::{BoxFuture, FutureExt, TryFutureExt};
2+
use http::status::StatusCode;
3+
use std::{
4+
any::Any,
5+
panic::{AssertUnwindSafe, RefUnwindSafe},
6+
};
7+
use tide_core::{
8+
middleware::{Middleware, Next},
9+
response::IntoResponse,
10+
Context, Response,
11+
};
12+
13+
/// A [`Middleware`] that will catch any panics from later middleware or handlers and return a
14+
/// response to the client.
15+
///
16+
/// It is **not** recommended to use this middleware for a general try/catch mechanism. The
17+
/// [`Result`] type is more appropriate to use for middleware/handlers that can fail on a regular
18+
/// basis. Additionally, this middleware is not guaranteed to catch all panics, see the "Notes"
19+
/// section in the [`std::panic::catch_unwind`] docs.
20+
pub struct CatchUnwind {
21+
f: Box<dyn Fn(Box<dyn Any + Send + 'static>) -> Response + Send + Sync>,
22+
}
23+
24+
impl CatchUnwind {
25+
/// Create a [`CatchUnwind`] which will respond with [`StatusCode::INTERNAL_SERVER_ERROR`] when
26+
/// any panic is caught.
27+
pub fn new() -> Self {
28+
Self::with_response(|_| {
29+
"Internal server error"
30+
.with_status(StatusCode::INTERNAL_SERVER_ERROR)
31+
.into_response()
32+
})
33+
}
34+
35+
/// Create a [`CatchUnwind`] with a custom function to generate the response, the function will
36+
/// be passed the caught panic.
37+
pub fn with_response(
38+
response: impl Fn(Box<dyn Any + Send + 'static>) -> Response + Send + Sync + 'static,
39+
) -> Self {
40+
Self {
41+
f: Box::new(response),
42+
}
43+
}
44+
}
45+
46+
impl<State: RefUnwindSafe + 'static> Middleware<State> for CatchUnwind {
47+
fn handle<'a>(&'a self, cx: Context<State>, next: Next<'a, State>) -> BoxFuture<'a, Response> {
48+
AssertUnwindSafe(next.run(cx))
49+
.catch_unwind()
50+
.unwrap_or_else(move |err| (self.f)(err))
51+
.boxed()
52+
}
53+
}
54+
55+
impl Default for CatchUnwind {
56+
fn default() -> Self {
57+
Self::new()
58+
}
59+
}
60+
61+
impl std::fmt::Debug for CatchUnwind {
62+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63+
f.debug_struct("CatchUnwind").finish()
64+
}
65+
}

tide-panic/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(async_await, doc_cfg)]
2+
#![warn(
3+
nonstandard_style,
4+
rust_2018_idioms,
5+
future_incompatible,
6+
missing_debug_implementations,
7+
missing_docs
8+
)]
9+
10+
//! Advanced panic support for Tide applications.
11+
//!
12+
//! These middleware should not generally be necessary, they are provided for situations in which
13+
//! Tide's default panic handling is not usable by your application. Before using these you should
14+
//! have a good understanding of how the different components involved in [`std::panic`] works.
15+
16+
mod catch_unwind;
17+
18+
pub use crate::catch_unwind::CatchUnwind;

0 commit comments

Comments
 (0)