Skip to main content

objectstore_server/extractors/
request_time.rs

1//! Request start time shared by object creation and expiry decisions.
2
3use std::convert::Infallible;
4
5use axum::extract::FromRequestParts;
6use axum::http::request::Parts;
7use objectstore_types::time::Timestamp;
8
9/// The request's creation and access timestamp.
10///
11/// Middleware extracts this at request entry. Subsequent extractors reuse the
12/// extension, including when a handler dispatches to another handler. Without
13/// the middleware, the first extraction captures the time instead.
14#[derive(Clone, Copy, Debug)]
15pub struct RequestTime(pub Timestamp);
16
17impl<S: Send + Sync> FromRequestParts<S> for RequestTime {
18    type Rejection = Infallible;
19
20    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
21        Ok(*parts
22            .extensions
23            .get_or_insert_with(|| Self(Timestamp::now())))
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use axum::extract::FromRequestParts;
30    use axum::http::Request;
31    use objectstore_types::time::Timestamp;
32
33    use super::RequestTime;
34
35    #[tokio::test]
36    async fn reuses_request_time() {
37        let (mut parts, ()) = Request::new(()).into_parts();
38        let first = RequestTime::from_request_parts(&mut parts, &())
39            .await
40            .unwrap();
41        assert_eq!(parts.extensions.get::<RequestTime>().unwrap().0, first.0);
42
43        parts.extensions.insert(RequestTime(Timestamp::UNIX_EPOCH));
44        let extracted = RequestTime::from_request_parts(&mut parts, &())
45            .await
46            .unwrap();
47        assert_eq!(extracted.0, Timestamp::UNIX_EPOCH);
48    }
49}