From 250482859623118cd7e5281146596247340406cd Mon Sep 17 00:00:00 2001 From: Nemo157 Date: Thu, 6 Jun 2019 01:48:54 +0200 Subject: [PATCH] Add middleware for catching panics (#265) --- Cargo.toml | 1 + tide-panic/Cargo.toml | 15 ++++++++ tide-panic/LICENSE-APACHE | 1 + tide-panic/LICENSE-MIT | 1 + tide-panic/src/catch_unwind.rs | 65 ++++++++++++++++++++++++++++++++++ tide-panic/src/lib.rs | 18 ++++++++++ 6 files changed, 101 insertions(+) create mode 100644 tide-panic/Cargo.toml create mode 120000 tide-panic/LICENSE-APACHE create mode 120000 tide-panic/LICENSE-MIT create mode 100644 tide-panic/src/catch_unwind.rs create mode 100644 tide-panic/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index f977500a5..543edf1b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ members = [ "tide-log", "tide-querystring", "tide-slog", + "tide-panic", ] [patch.crates-io] diff --git a/tide-panic/Cargo.toml b/tide-panic/Cargo.toml new file mode 100644 index 000000000..0937c822c --- /dev/null +++ b/tide-panic/Cargo.toml @@ -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" } diff --git a/tide-panic/LICENSE-APACHE b/tide-panic/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/tide-panic/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/tide-panic/LICENSE-MIT b/tide-panic/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/tide-panic/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/tide-panic/src/catch_unwind.rs b/tide-panic/src/catch_unwind.rs new file mode 100644 index 000000000..eb5726a7f --- /dev/null +++ b/tide-panic/src/catch_unwind.rs @@ -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) -> 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) -> Response + Send + Sync + 'static, + ) -> Self { + Self { + f: Box::new(response), + } + } +} + +impl Middleware for CatchUnwind { + fn handle<'a>(&'a self, cx: Context, 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() + } +} diff --git a/tide-panic/src/lib.rs b/tide-panic/src/lib.rs new file mode 100644 index 000000000..530f264b0 --- /dev/null +++ b/tide-panic/src/lib.rs @@ -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;