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
use super::{OnBodyChunk, OnEos, OnFailure};
use crate::classify::ClassifyEos;
use futures_core::ready;
use http::HeaderMap;
use http_body::Body;
use pin_project_lite::pin_project;
use std::{
fmt,
pin::Pin,
task::{Context, Poll},
time::Instant,
};
use tracing::Span;
pin_project! {
pub struct ResponseBody<B, C, OnBodyChunk, OnEos, OnFailure> {
#[pin]
pub(crate) inner: B,
pub(crate) classify_eos: Option<C>,
pub(crate) on_eos: Option<(OnEos, Instant)>,
pub(crate) on_body_chunk: OnBodyChunk,
pub(crate) on_failure: Option<OnFailure>,
pub(crate) start: Instant,
pub(crate) span: Span,
}
}
impl<B, C, OnBodyChunkT, OnEosT, OnFailureT> Body
for ResponseBody<B, C, OnBodyChunkT, OnEosT, OnFailureT>
where
B: Body,
B::Error: fmt::Display + 'static,
C: ClassifyEos,
OnEosT: OnEos,
OnBodyChunkT: OnBodyChunk<B::Data>,
OnFailureT: OnFailure<C::FailureClass>,
{
type Data = B::Data;
type Error = B::Error;
fn poll_data(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
let this = self.project();
let _guard = this.span.enter();
let result = if let Some(result) = ready!(this.inner.poll_data(cx)) {
result
} else {
return Poll::Ready(None);
};
let latency = this.start.elapsed();
*this.start = Instant::now();
match &result {
Ok(chunk) => {
this.on_body_chunk.on_body_chunk(chunk, latency, this.span);
}
Err(err) => {
if let Some((classify_eos, mut on_failure)) =
this.classify_eos.take().zip(this.on_failure.take())
{
let failure_class = classify_eos.classify_error(err);
on_failure.on_failure(failure_class, latency, this.span);
}
}
}
Poll::Ready(Some(result))
}
fn poll_trailers(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<Option<HeaderMap>, Self::Error>> {
let this = self.project();
let _guard = this.span.enter();
let result = ready!(this.inner.poll_trailers(cx));
let latency = this.start.elapsed();
if let Some((classify_eos, mut on_failure)) =
this.classify_eos.take().zip(this.on_failure.take())
{
match &result {
Ok(trailers) => {
if let Err(failure_class) = classify_eos.classify_eos(trailers.as_ref()) {
on_failure.on_failure(failure_class, latency, this.span);
}
if let Some((on_eos, stream_start)) = this.on_eos.take() {
on_eos.on_eos(trailers.as_ref(), stream_start.elapsed(), this.span);
}
}
Err(err) => {
let failure_class = classify_eos.classify_error(err);
on_failure.on_failure(failure_class, latency, this.span);
}
}
}
Poll::Ready(result)
}
fn is_end_stream(&self) -> bool {
self.inner.is_end_stream()
}
fn size_hint(&self) -> http_body::SizeHint {
self.inner.size_hint()
}
}