Skip to content

Commit bdaf9e4

Browse files
committed
Timings
1 parent d0dde3e commit bdaf9e4

File tree

1 file changed

+84
-2
lines changed

1 file changed

+84
-2
lines changed

crates/twirp/src/server.rs

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use futures::Future;
66
use hyper::{header, Body, Method, Request, Response};
77
use serde::de::DeserializeOwned;
88
use serde::Serialize;
9+
use tokio::time::{Duration, Instant};
910

1011
use crate::error::*;
1112
use crate::headers::*;
@@ -100,19 +101,35 @@ impl Router {
100101
> {
101102
let f = f.clone();
102103
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();
103109
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+
}
105116
Err(err) => {
106117
// This is the only place we use tracing (would be nice to remove)
107118
// tracing::error!(?err, "failed to parse request");
108119
// TODO: We don't want to loose the underlying error
109120
// here, but it might not be safe to include in the
110121
// response like this always.
122+
timings.request_parsed();
111123
let mut twirp_err = malformed("bad request");
112124
twirp_err.insert_meta("error".to_string(), err.to_string());
113125
twirp_err.to_response()
114126
}
115127
}
128+
.map(|mut resp| {
129+
timings.response_written();
130+
resp.extensions_mut().insert(timings);
131+
resp
132+
})
116133
}))
117134
};
118135
let key = (Method::POST, [self.prefix, path].join("/"));
@@ -123,8 +140,9 @@ impl Router {
123140
/// Serve a request using the given router.
124141
pub async fn serve(
125142
router: Arc<Router>,
126-
req: Request<Body>,
143+
mut req: Request<Body>,
127144
) -> Result<Response<Body>, GenericError> {
145+
req.extensions_mut().insert(Timings::default());
128146
let key = (req.method().clone(), req.uri().path().to_string());
129147
if let Some(handler) = router.routes.get(&key) {
130148
handler(req).await
@@ -196,6 +214,70 @@ where
196214
Ok(res)
197215
}
198216

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+
199281
#[cfg(test)]
200282
mod tests {
201283

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy