Skip to content

Commit

Permalink
Add middleware for catching panics (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nemo157 authored and yoshuawuyts committed Jun 5, 2019
1 parent 7c2046a commit 2504828
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ members = [
"tide-log",
"tide-querystring",
"tide-slog",
"tide-panic",
]

[patch.crates-io]
Expand Down
15 changes: 15 additions & 0 deletions tide-panic/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "tide-panic"
version = "0.1.0"
edition = "2018"
authors = ["Tide Developers"]
description = "Advanced panic support for Tide"
documentation = "https://docs.rs/tide-panic"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rustasync/tide"

[dependencies]
futures-preview = "0.3.0-alpha.16"
http = "0.1"
http-service = "0.2.0"
tide-core = { path = "../tide-core" }
1 change: 1 addition & 0 deletions tide-panic/LICENSE-APACHE
1 change: 1 addition & 0 deletions tide-panic/LICENSE-MIT
65 changes: 65 additions & 0 deletions tide-panic/src/catch_unwind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use futures::future::{BoxFuture, FutureExt, TryFutureExt};
use http::status::StatusCode;
use std::{
any::Any,
panic::{AssertUnwindSafe, RefUnwindSafe},
};
use tide_core::{
middleware::{Middleware, Next},
response::IntoResponse,
Context, Response,
};

/// A [`Middleware`] that will catch any panics from later middleware or handlers and return a
/// response to the client.
///
/// It is **not** recommended to use this middleware for a general try/catch mechanism. The
/// [`Result`] type is more appropriate to use for middleware/handlers that can fail on a regular
/// basis. Additionally, this middleware is not guaranteed to catch all panics, see the "Notes"
/// section in the [`std::panic::catch_unwind`] docs.
pub struct CatchUnwind {
f: Box<dyn Fn(Box<dyn Any + Send + 'static>) -> Response + Send + Sync>,
}

impl CatchUnwind {
/// Create a [`CatchUnwind`] which will respond with [`StatusCode::INTERNAL_SERVER_ERROR`] when
/// any panic is caught.
pub fn new() -> Self {
Self::with_response(|_| {
"Internal server error"
.with_status(StatusCode::INTERNAL_SERVER_ERROR)
.into_response()
})
}

/// Create a [`CatchUnwind`] with a custom function to generate the response, the function will
/// be passed the caught panic.
pub fn with_response(
response: impl Fn(Box<dyn Any + Send + 'static>) -> Response + Send + Sync + 'static,
) -> Self {
Self {
f: Box::new(response),
}
}
}

impl<State: RefUnwindSafe + 'static> Middleware<State> for CatchUnwind {
fn handle<'a>(&'a self, cx: Context<State>, next: Next<'a, State>) -> BoxFuture<'a, Response> {
AssertUnwindSafe(next.run(cx))
.catch_unwind()
.unwrap_or_else(move |err| (self.f)(err))
.boxed()
}
}

impl Default for CatchUnwind {
fn default() -> Self {
Self::new()
}
}

impl std::fmt::Debug for CatchUnwind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CatchUnwind").finish()
}
}
18 changes: 18 additions & 0 deletions tide-panic/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#![feature(async_await, doc_cfg)]
#![warn(
nonstandard_style,
rust_2018_idioms,
future_incompatible,
missing_debug_implementations,
missing_docs
)]

//! Advanced panic support for Tide applications.
//!
//! These middleware should not generally be necessary, they are provided for situations in which
//! Tide's default panic handling is not usable by your application. Before using these you should
//! have a good understanding of how the different components involved in [`std::panic`] works.
mod catch_unwind;

pub use crate::catch_unwind::CatchUnwind;

0 comments on commit 2504828

Please sign in to comment.