@@ -6,6 +6,7 @@ use futures::Future;
6
6
use hyper:: { header, Body , Method , Request , Response } ;
7
7
use serde:: de:: DeserializeOwned ;
8
8
use serde:: Serialize ;
9
+ use tokio:: time:: { Duration , Instant } ;
9
10
10
11
use crate :: error:: * ;
11
12
use crate :: headers:: * ;
@@ -100,19 +101,35 @@ impl Router {
100
101
> {
101
102
let f = f. clone ( ) ;
102
103
Box :: new ( Box :: pin ( async move {
104
+ let mut timings = * req
105
+ . extensions ( )
106
+ . get :: < Timings > ( )
107
+ . expect ( "timings must exist" ) ;
108
+ timings. request_received ( ) ;
103
109
match parse_request ( req) . await {
104
- Ok ( ( req, resp_fmt) ) => write_response ( f ( req) . await , resp_fmt) ,
110
+ Ok ( ( req, resp_fmt) ) => {
111
+ timings. request_parsed ( ) ;
112
+ let res = f ( req) . await ;
113
+ timings. response_handled ( ) ;
114
+ write_response ( res, resp_fmt)
115
+ }
105
116
Err ( err) => {
106
117
// This is the only place we use tracing (would be nice to remove)
107
118
// tracing::error!(?err, "failed to parse request");
108
119
// TODO: We don't want to loose the underlying error
109
120
// here, but it might not be safe to include in the
110
121
// response like this always.
122
+ timings. request_parsed ( ) ;
111
123
let mut twirp_err = malformed ( "bad request" ) ;
112
124
twirp_err. insert_meta ( "error" . to_string ( ) , err. to_string ( ) ) ;
113
125
twirp_err. to_response ( )
114
126
}
115
127
}
128
+ . map ( |mut resp| {
129
+ timings. response_written ( ) ;
130
+ resp. extensions_mut ( ) . insert ( timings) ;
131
+ resp
132
+ } )
116
133
} ) )
117
134
} ;
118
135
let key = ( Method :: POST , [ self . prefix , path] . join ( "/" ) ) ;
@@ -123,8 +140,9 @@ impl Router {
123
140
/// Serve a request using the given router.
124
141
pub async fn serve (
125
142
router : Arc < Router > ,
126
- req : Request < Body > ,
143
+ mut req : Request < Body > ,
127
144
) -> Result < Response < Body > , GenericError > {
145
+ req. extensions_mut ( ) . insert ( Timings :: default ( ) ) ;
128
146
let key = ( req. method ( ) . clone ( ) , req. uri ( ) . path ( ) . to_string ( ) ) ;
129
147
if let Some ( handler) = router. routes . get ( & key) {
130
148
handler ( req) . await
@@ -196,6 +214,70 @@ where
196
214
Ok ( res)
197
215
}
198
216
217
+ #[ derive( Debug , Clone , Copy ) ]
218
+ pub struct Timings {
219
+ // When the request started.
220
+ pub start : Instant ,
221
+ // When the request was received.
222
+ pub request_received : Option < Instant > ,
223
+ // When the request body was parsed.
224
+ pub request_parsed : Option < Instant > ,
225
+ // When the response handler returned.
226
+ pub response_handled : Option < Instant > ,
227
+ // When the response was written.
228
+ pub response_written : Option < Instant > ,
229
+ }
230
+
231
+ impl Default for Timings {
232
+ fn default ( ) -> Self {
233
+ Self :: new ( Instant :: now ( ) )
234
+ }
235
+ }
236
+
237
+ impl Timings {
238
+ pub fn new ( start : Instant ) -> Self {
239
+ Self {
240
+ start,
241
+ request_received : None ,
242
+ request_parsed : None ,
243
+ response_handled : None ,
244
+ response_written : None ,
245
+ }
246
+ }
247
+
248
+ pub fn received ( & self ) -> Option < Duration > {
249
+ self . request_received . map ( |x| x - self . start )
250
+ }
251
+
252
+ fn request_received ( & mut self ) {
253
+ self . request_received = Some ( Instant :: now ( ) ) ;
254
+ }
255
+
256
+ pub fn parsed ( & self ) -> Option < Duration > {
257
+ self . request_parsed . map ( |x| x - self . start )
258
+ }
259
+
260
+ fn request_parsed ( & mut self ) {
261
+ self . request_parsed = Some ( Instant :: now ( ) ) ;
262
+ }
263
+
264
+ pub fn handled ( & self ) -> Option < Duration > {
265
+ self . response_handled . map ( |x| x - self . start )
266
+ }
267
+
268
+ fn response_handled ( & mut self ) {
269
+ self . response_handled = Some ( Instant :: now ( ) ) ;
270
+ }
271
+
272
+ pub fn written ( & self ) -> Option < Duration > {
273
+ self . response_written . map ( |x| x - self . start )
274
+ }
275
+
276
+ fn response_written ( & mut self ) {
277
+ self . response_written = Some ( Instant :: now ( ) ) ;
278
+ }
279
+ }
280
+
199
281
#[ cfg( test) ]
200
282
mod tests {
201
283
0 commit comments