@@ -11,32 +11,31 @@ use std::{
11
11
task:: { Context , Poll } ,
12
12
} ;
13
13
14
- /// An HTTP body that allows inspecting the body's trailers, if a `TRAILERS`
15
- /// frame was the first frame after the initial headers frame.
14
+ /// An HTTP body that allows inspecting the body's trailers.
16
15
///
17
- /// If the first frame of the body stream was *not* a `TRAILERS` frame, this
18
- /// behaves identically to a normal body.
19
- pub struct PeekTrailersBody < B : Body = BoxBody > {
20
- inner : B ,
21
-
22
- /// The first DATA frame received from the inner body, or an error that
23
- /// occurred while polling for data.
24
- ///
25
- /// If this is `None`, then the body has completed without any DATA frames.
26
- first_data : Option < Result < B :: Data , B :: Error > > ,
27
-
28
- /// The inner body's trailers, if it was terminated by a `TRAILERS` frame
29
- /// after 0-1 DATA frames, or an error if polling for trailers failed.
16
+ /// The body's trailers may be peeked with [`PeekTrailersBody::peek_trailers()`].
17
+ ///
18
+ /// Trailers may only be peeked if the inner body immediately yields a TRAILERS frame. If the first
19
+ /// frame of the body stream was *not* a `TRAILERS` frame, this behaves identically to a normal
20
+ /// body.
21
+ pub enum PeekTrailersBody < B : Body = BoxBody > {
22
+ /// The trailers are not available to be inspected.
23
+ Passthru {
24
+ /// The inner body.
25
+ inner : B ,
26
+ /// The first DATA frame received from the inner body, or an error that
27
+ /// occurred while polling for data.
28
+ ///
29
+ /// If this is `None`, then the body has completed without any DATA frames.
30
+ first_data : Option < Result < B :: Data , B :: Error > > ,
31
+ } ,
32
+ /// The trailers have been peeked.
30
33
///
31
- /// Yes, this is a bit of a complex type, so let's break it down:
32
- /// - the outer `Option` indicates whether any trailers were received by
33
- /// `WithTrailers`; if it's `None`, then we don't *know* if the response
34
- /// had trailers, as it is not yet complete.
35
- /// - the inner `Result` and `Option` are the `Result` and `Option` returned
36
- /// by `HttpBody::trailers` on the inner body. If this is `Ok(None)`, then
37
- /// the body has terminated without trailers --- it is *known* to not have
38
- /// trailers.
39
- trailers : Option < Result < Option < http:: HeaderMap > , B :: Error > > ,
34
+ /// This variant applies if the inner body's first frame was a `TRAILERS` frame.
35
+ Peek {
36
+ /// The inner body's trailers.
37
+ trailers : Option < Result < Option < http:: HeaderMap > , B :: Error > > ,
38
+ } ,
40
39
}
41
40
42
41
pub type WithPeekTrailersBody < B > = Either <
@@ -50,10 +49,16 @@ pub struct ResponseWithPeekTrailers<S>(pub(crate) S);
50
49
// === impl WithTrailers ===
51
50
52
51
impl < B : Body > PeekTrailersBody < B > {
52
+ /// Returns a reference to the trailers, if applicable.
53
+ ///
54
+ /// See [`PeekTrailersBody<B>`] for more information on when this returns `None`.
53
55
pub fn peek_trailers ( & self ) -> Option < & http:: HeaderMap > {
54
- self . trailers
55
- . as_ref ( )
56
- . and_then ( |trls| trls. as_ref ( ) . ok ( ) ?. as_ref ( ) )
56
+ match self {
57
+ Self :: Peek {
58
+ trailers : Some ( Ok ( Some ( ref t) ) ) ,
59
+ } => Some ( t) ,
60
+ Self :: Passthru { .. } | Self :: Peek { .. } => None ,
61
+ }
57
62
}
58
63
59
64
pub fn map_response ( rsp : http:: Response < B > ) -> WithPeekTrailersBody < B >
@@ -87,44 +92,32 @@ impl<B: Body> PeekTrailersBody<B> {
87
92
B :: Data : Send + Unpin ,
88
93
B :: Error : Send ,
89
94
{
90
- let ( parts, body) = rsp. into_parts ( ) ;
91
- let mut body = Self {
92
- inner : body,
93
- first_data : None ,
94
- trailers : None ,
95
- } ;
95
+ let ( parts, mut body) = rsp. into_parts ( ) ;
96
96
97
- tracing:: debug!( "Buffering first data frame" ) ;
98
- if let Some ( data ) = body. inner . data ( ) . await {
97
+ tracing:: debug!( "Buffering first body frame" ) ;
98
+ if let first_data @ Some ( _ ) = body. data ( ) . await {
99
99
// The body has data; stop waiting for trailers.
100
- body. first_data = Some ( data) ;
101
-
102
- // Peek to see if there's immediately a trailers frame, and grab
103
- // it if so. Otherwise, bail.
104
- // XXX(eliza): the documentation for the `http::Body` trait says
105
- // that `poll_trailers` should only be called after `poll_data`
106
- // returns `None`...but, in practice, I'm fairly sure that this just
107
- // means that it *will not return `Ready`* until there are no data
108
- // frames left, which is fine for us here, because we `now_or_never`
109
- // it.
110
- body. trailers = body. inner . trailers ( ) . now_or_never ( ) ;
111
- } else {
112
- // Okay, `poll_data` has returned `None`, so there are no data
113
- // frames left. Let's see if there's trailers...
114
- body. trailers = Some ( body. inner . trailers ( ) . await ) ;
115
- }
116
- if body. trailers . is_some ( ) {
117
- tracing:: debug!( "Buffered trailers frame" ) ;
100
+ let body = Self :: Passthru {
101
+ inner : body,
102
+ first_data,
103
+ } ;
104
+ return http:: Response :: from_parts ( parts, body) ;
118
105
}
119
106
107
+ // We have confirmed that there are no data frames. Now, await the trailers.
108
+ let trailers = body. trailers ( ) . await ;
109
+ tracing:: debug!( "Buffered trailers frame" ) ;
110
+ let body = Self :: Peek {
111
+ trailers : Some ( trailers) ,
112
+ } ;
120
113
http:: Response :: from_parts ( parts, body)
121
114
}
122
115
116
+ /// Returns a response with an inert [`PeekTrailersBody<B>`].
123
117
fn no_trailers ( rsp : http:: Response < B > ) -> http:: Response < Self > {
124
- rsp. map ( |inner| Self {
118
+ rsp. map ( |inner| Self :: Passthru {
125
119
inner,
126
120
first_data : None ,
127
- trailers : None ,
128
121
} )
129
122
}
130
123
}
@@ -142,47 +135,67 @@ where
142
135
self : Pin < & mut Self > ,
143
136
cx : & mut Context < ' _ > ,
144
137
) -> Poll < Option < Result < Self :: Data , Self :: Error > > > {
145
- let this = self . get_mut ( ) ;
146
- if let Some ( first_data) = this. first_data . take ( ) {
147
- return Poll :: Ready ( Some ( first_data) ) ;
138
+ match self . get_mut ( ) {
139
+ Self :: Passthru {
140
+ first_data,
141
+ ref mut inner,
142
+ } => {
143
+ // Return the first chunk that was buffered originally.
144
+ if let data @ Some ( _) = first_data. take ( ) {
145
+ return Poll :: Ready ( data) ;
146
+ }
147
+ // ...and then, poll the inner body.
148
+ Pin :: new ( inner) . poll_data ( cx)
149
+ }
150
+ // If we have peeked the trailers, we've already polled an empty body.
151
+ Self :: Peek { .. } => Poll :: Ready ( None ) ,
148
152
}
149
-
150
- Pin :: new ( & mut this. inner ) . poll_data ( cx)
151
153
}
152
154
153
155
fn poll_trailers (
154
156
self : Pin < & mut Self > ,
155
157
cx : & mut Context < ' _ > ,
156
158
) -> Poll < Result < Option < http:: HeaderMap > , Self :: Error > > {
157
- let this = self . get_mut ( ) ;
158
- if let Some ( trailers) = this. trailers . take ( ) {
159
- return Poll :: Ready ( trailers) ;
159
+ match self . get_mut ( ) {
160
+ Self :: Passthru { ref mut inner, .. } => Pin :: new ( inner) . poll_trailers ( cx) ,
161
+ Self :: Peek { trailers } => {
162
+ let trailers = trailers
163
+ . take ( )
164
+ . expect ( "poll_trailers should not be called more than once" ) ;
165
+ Poll :: Ready ( trailers)
166
+ }
160
167
}
161
-
162
- Pin :: new ( & mut this. inner ) . poll_trailers ( cx)
163
168
}
164
169
165
170
#[ inline]
166
171
fn is_end_stream ( & self ) -> bool {
167
- self . first_data . is_none ( ) && self . trailers . is_none ( ) && self . inner . is_end_stream ( )
172
+ match self {
173
+ Self :: Passthru { inner, first_data } => first_data. is_none ( ) && inner. is_end_stream ( ) ,
174
+ Self :: Peek { trailers : Some ( _) } => false ,
175
+ Self :: Peek { trailers : None } => true ,
176
+ }
168
177
}
169
178
170
179
#[ inline]
171
180
fn size_hint ( & self ) -> http_body:: SizeHint {
172
181
use bytes:: Buf ;
173
182
174
- let mut hint = self . inner . size_hint ( ) ;
175
- // If we're holding onto a chunk of data, add its length to the inner
176
- // `Body`'s size hint.
177
- if let Some ( Ok ( chunk) ) = self . first_data . as_ref ( ) {
178
- let buffered = chunk. remaining ( ) as u64 ;
179
- if let Some ( upper) = hint. upper ( ) {
180
- hint. set_upper ( upper + buffered) ;
183
+ match self {
184
+ Self :: Passthru { inner, first_data } => {
185
+ let mut hint = inner. size_hint ( ) ;
186
+ // If we're holding onto a chunk of data, add its length to the inner
187
+ // `Body`'s size hint.
188
+ if let Some ( Ok ( chunk) ) = first_data. as_ref ( ) {
189
+ let buffered = chunk. remaining ( ) as u64 ;
190
+ if let Some ( upper) = hint. upper ( ) {
191
+ hint. set_upper ( upper + buffered) ;
192
+ }
193
+ hint. set_lower ( hint. lower ( ) + buffered) ;
194
+ }
195
+ hint
181
196
}
182
- hint . set_lower ( hint . lower ( ) + buffered ) ;
197
+ Self :: Peek { .. } => http_body :: SizeHint :: default ( ) ,
183
198
}
184
-
185
- hint
186
199
}
187
200
}
188
201
0 commit comments