1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Request lifecycle hooks that can be used to further customize how and what callbacks to run
//! on events.
//!
//! `axum-prometheus` is built on top of lifecycle hooks. Using this module allows you to customize
//! behavior even more.
use bytes::Buf;
use http::{HeaderMap, Request, Response};
use tower_http::classify::ClassifiedResponse;

mod body;
mod future;
pub mod layer;
pub mod service;

/// Trait that defines callbacks for [`LifeCycle`] to call.
///
/// [`LifeCycle`]: service::LifeCycle
pub trait Callbacks<FailureClass>: Sized {
    /// Additional data to attach to callbacks.
    type Data;

    /// Create an instance of `Self::Data` from the request.
    ///
    /// This method is called immediately after the request is received by [`Service::call`].
    ///
    /// The value returned here will be passed to the other methods in this trait.
    ///
    /// [`Service::call`]: tower::Service::call
    fn prepare<B>(&mut self, request: &Request<B>) -> Self::Data;

    /// Perform some action when a response has been generated.
    ///
    /// This method is called when the inner [`Service`]'s response future
    /// completes with `Ok(response)`, regardless if the response is classified
    /// as a success or a failure.
    ///
    /// If the response is the start of a stream (as determined by the
    /// classifier passed to [`LifeCycle::new`] or [`LifeCycleLayer::new`]) then
    /// `classification` will be [`ClassifiedResponse::RequiresEos(())`],
    /// otherwise it will be [`ClassifiedResponse::Ready`].
    ///
    /// The default implementation does nothing and returns immediately.
    ///
    /// [`ClassifiedResponse::RequiresEos(())`]: tower_http::classify::ClassifiedResponse::RequiresEos
    /// [`Service`]: tower::Service
    /// [`LifeCycle::new`]: service::LifeCycle::new
    /// [`LifeCycleLayer::new`]: layer::LifeCycleLayer::new
    #[inline]
    fn on_response<B>(
        &mut self,
        _response: &Response<B>,
        _classification: ClassifiedResponse<FailureClass, ()>,
        _data: &mut Self::Data,
    ) {
    }

    /// Perform some action when a response body chunk has been generated.
    ///
    /// This is called when [`Body::poll_data`] completes with `Some(Ok(chunk))`
    /// regardless if the chunk is empty or not.
    ///
    /// The default implementation does nothing and returns immediately.
    ///
    /// [`Body::poll_data`]: http_body::Body::poll_data
    #[inline]
    fn on_body_chunk<B: Buf>(&self, _check: &B, _data: &Self::Data) {}

    /// Perform some action when a stream has ended.
    ///
    /// This is called when [`Body::poll_trailers`] completes with
    /// `Ok(trailers)` regardless if the trailers are classified as a failure.
    ///
    /// A stream that ends successfully will trigger two callbacks.
    /// [`on_response`] will be called once the response has been generated and
    /// the stream has started and [`on_eos`] will be called once the stream has
    /// ended.
    ///
    /// If the trailers were classified as a success then `classification` will
    /// be `Ok(())` otherwise `Err(failure_class)`.
    ///
    /// The default implementation does nothing and returns immediately.
    ///
    /// [`on_response`]: Callbacks::on_response
    /// [`on_eos`]: Callbacks::on_eos
    /// [`Body::poll_trailers`]: http_body::Body::poll_trailers
    #[inline]
    fn on_eos(
        self,
        _trailers: Option<&HeaderMap>,
        _classification: Result<(), FailureClass>,
        _data: Self::Data,
    ) {
    }

    /// Perform some action when an error has been encountered.
    ///
    /// This method is only called in the following scenarios:
    ///
    /// - The inner [`Service`]'s response future resolves to an error.
    /// - [`Body::poll_data`] returns an error.
    /// - [`Body::poll_trailers`] returns an error.
    ///
    /// That means this method is _not_ called if a response is classified as a
    /// failure (then [`on_response`] is called) or an end-of-stream is
    /// classified as a failure (then [`on_eos`] is called).
    ///
    /// `failed_at` specifies where the error happened.
    ///
    /// The default implementation does nothing and returns immediately.
    ///
    /// [`Service`]: tower::Service
    /// [`on_response`]: Callbacks::on_response
    /// [`on_eos`]: Callbacks::on_eos
    /// [`Service::call`]: tower::Service::call
    /// [`Body::poll_data`]: http_body::Body::poll_data
    /// [`Body::poll_trailers`]: http_body::Body::poll_trailers
    fn on_failure(
        self,
        _failed_at: FailedAt,
        _failure_classification: FailureClass,
        _data: Self::Data,
    ) {
    }
}

/// Enum used to specify where an error was encountered.
#[derive(Debug)]
pub enum FailedAt {
    /// Generating the response failed.
    Response,
    /// Generating the response body failed.
    Body,
    /// Generating the response trailers failed.
    Trailers,
}