Skip to main content

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}