tower_http/on_early_drop/failure.rs
1//! Failure classifications produced by [`EarlyDropsAsFailures`].
2//!
3//! [`EarlyDropsAsFailures`]: super::EarlyDropsAsFailures
4
5use http::StatusCode;
6use std::fmt;
7
8/// Classification for early-drop events reported through
9/// [`EarlyDropsAsFailures`].
10///
11/// # Example
12///
13/// ```
14/// use std::time::Duration;
15/// use tower_http::on_early_drop::DroppedFailure;
16/// use tower_http::trace::OnFailure;
17/// use tracing::Span;
18///
19/// #[derive(Clone)]
20/// struct MyOnFailure;
21///
22/// impl OnFailure<DroppedFailure> for MyOnFailure {
23/// fn on_failure(&mut self, class: DroppedFailure, latency: Duration, _span: &Span) {
24/// match class {
25/// DroppedFailure::Future(_) => {
26/// tracing::warn!(?latency, "future dropped")
27/// }
28/// DroppedFailure::Body(body) => {
29/// tracing::warn!(?latency, status = %body.status, "body dropped")
30/// }
31/// _ => {}
32/// }
33/// }
34/// }
35/// ```
36///
37/// [`EarlyDropsAsFailures`]: super::EarlyDropsAsFailures
38#[derive(Debug, Clone)]
39#[non_exhaustive]
40pub enum DroppedFailure {
41 /// The response future was dropped before producing any response.
42 Future(FutureDropped),
43 /// The response body was dropped before reaching end-of-stream.
44 Body(BodyDropped),
45}
46
47/// Context for [`DroppedFailure::Future`].
48#[derive(Debug, Clone)]
49#[non_exhaustive]
50pub struct FutureDropped;
51
52/// Context for [`DroppedFailure::Body`].
53#[derive(Debug, Clone)]
54#[non_exhaustive]
55pub struct BodyDropped {
56 /// Status of the already-emitted response.
57 pub status: StatusCode,
58}
59
60impl fmt::Display for DroppedFailure {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 match self {
63 DroppedFailure::Future(_) => f.write_str("response future dropped before completion"),
64 DroppedFailure::Body(body) => {
65 write!(
66 f,
67 "response body dropped before end-of-stream (status: {})",
68 body.status
69 )
70 }
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn future_display() {
81 assert_eq!(
82 DroppedFailure::Future(FutureDropped).to_string(),
83 "response future dropped before completion",
84 );
85 }
86
87 #[test]
88 fn body_display_carries_status() {
89 assert_eq!(
90 DroppedFailure::Body(BodyDropped {
91 status: StatusCode::INTERNAL_SERVER_ERROR
92 })
93 .to_string(),
94 "response body dropped before end-of-stream (status: 500 Internal Server Error)",
95 );
96 }
97}