Skip to main content

tower_http/on_early_drop/
mod.rs

1//! Middleware that detects when a response future or response body is
2//! dropped before completion.
3//!
4//! HTTP services typically learn nothing when a client disconnects
5//! mid-request. This middleware installs drop guards so that premature
6//! termination is observable: logs, metrics, cleanup.
7//!
8//! Two events are distinguished:
9//!
10//! * **Future drop**: the response future was dropped before the inner
11//!   service produced any response.
12//! * **Body drop**: the response body was dropped before reaching
13//!   [`is_end_stream`](http_body::Body::is_end_stream).
14//!
15//! # Example: bridge to [`trace::OnFailure`](crate::trace::OnFailure)
16//!
17//! With the `trace` feature, [`EarlyDropsAsFailures`] wraps any
18//! [`OnFailure<DroppedFailure>`](crate::trace::OnFailure) and routes both
19//! events through it. Place this layer inside [`TraceLayer`](crate::trace::TraceLayer)
20//! so that the emitted events inherit the request span.
21//!
22//! ```
23//! # #[cfg(feature = "trace")] {
24//! use tower_http::on_early_drop::{OnEarlyDropLayer, EarlyDropsAsFailures};
25//! use tower_http::trace::DefaultOnFailure;
26//!
27//! let layer = OnEarlyDropLayer::new(
28//!     EarlyDropsAsFailures::new(DefaultOnFailure::default()),
29//! );
30//! # }
31//! ```
32//!
33//! Use [`OnEarlyDropLayer::builder`] to place [`EarlyDropsAsFailures`] in a
34//! single slot (track only body or only future drops) or to install plain
35//! closures.
36//!
37//! # Example: builder with direct callbacks
38//!
39//! Use [`OnEarlyDropLayer::builder`] to install closures in either or both
40//! slots. The future-drop closure is a factory: outer closure runs at
41//! request time, inner closure fires on drop. The body-drop closure uses a
42//! three-level chain via [`OnBodyDropFn`]: outer at request time, middle at
43//! response-ready time, inner on drop.
44//!
45//! ```
46//! use http::Request;
47//! use tower_http::on_early_drop::{OnBodyDropFn, OnEarlyDropLayer};
48//!
49//! let layer = OnEarlyDropLayer::builder()
50//!     .on_future_drop(|req: &Request<()>| {
51//!         let uri = req.uri().clone();
52//!         move || eprintln!("future dropped for {}", uri)
53//!     })
54//!     .on_body_drop(OnBodyDropFn::new(|req: &Request<()>| {
55//!         let uri = req.uri().clone();
56//!         move |parts: &http::response::Parts| {
57//!             let status = parts.status;
58//!             move || eprintln!("body dropped for {} status {}", uri, status)
59//!         }
60//!     }));
61//! ```
62//!
63//! Chain just one of the two methods to hook only that event; the other
64//! slot stays no-op.
65//!
66//! # Panics in callbacks
67//!
68//! Callbacks fire from [`Drop`]. Panicking during a drop that occurs while
69//! another panic is unwinding aborts the process. Closures and custom
70//! [`OnDropCallback`] implementations must not panic.
71//!
72//! # Standalone guard
73//!
74//! [`OnEarlyDropGuard`] is usable on its own to detect early drop of
75//! arbitrary scopes.
76//!
77//! ```
78//! use tower_http::on_early_drop::OnEarlyDropGuard;
79//!
80//! let mut guard = OnEarlyDropGuard::new(|| {
81//!     eprintln!("scope exited early");
82//! });
83//! // ... work that might return early ...
84//! guard.completed();
85//! ```
86
87mod body;
88mod failure;
89mod future;
90mod guard;
91mod layer;
92mod service;
93mod traits;
94
95#[cfg(feature = "trace")]
96mod early_drops_as_failures;
97
98pub use self::{
99    body::OnEarlyDropBody,
100    failure::{BodyDropped, DroppedFailure, FutureDropped},
101    future::OnEarlyDropFuture,
102    guard::OnEarlyDropGuard,
103    layer::OnEarlyDropLayer,
104    service::OnEarlyDropService,
105    traits::{NoopDropCallback, OnBodyDrop, OnBodyDropFn, OnDropCallback, OnFutureDrop},
106};
107
108#[cfg(feature = "trace")]
109pub use self::early_drops_as_failures::{
110    BodyDropFailureCallback, EarlyDropsAsFailures, FutureDropFailureCallback,
111    PreResponseBodyDropCallback,
112};