From ad27bcdbd04e1c78b4471cbe22a9a8f93b880d7f Mon Sep 17 00:00:00 2001 From: Philip Jenvey Date: Fri, 30 Nov 2018 11:21:33 -0800 Subject: [PATCH] fix: use SyncTimestamp for sane WeaveTimestamp comparisons to match the level of timestamp precision. also have its middleware run first to match python --- src/db/util.rs | 7 ++++++- src/server/mod.rs | 2 +- src/web/middleware.rs | 29 ++++++++++++++++------------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/db/util.rs b/src/db/util.rs index b4901a88..c3f5ea00 100644 --- a/src/db/util.rs +++ b/src/db/util.rs @@ -75,10 +75,15 @@ impl SyncTimestamp { SyncTimestamp(val - (val % 10)) } - /// Return the timestamp as an i64 milliseconds + /// Return the timestamp as an i64 milliseconds since epoch pub fn as_i64(&self) -> i64 { self.0 as i64 } + + /// Return the timestamp as an f64 seconds since epoch + pub fn as_seconds(&self) -> f64 { + self.0 as f64 / 1000.0 + } } impl Default for SyncTimestamp { diff --git a/src/server/mod.rs b/src/server/mod.rs index 29ec496e..7949a4c3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -63,8 +63,8 @@ pub struct ServerState { pub fn build_app(state: ServerState) -> App { App::with_state(state) - .middleware(middleware::DbTransaction) .middleware(middleware::WeaveTimestamp) + .middleware(middleware::DbTransaction) .middleware(middleware::PreConditionCheck) .configure(|app| init_routes!(Cors::for_app(app)).register()) } diff --git a/src/web/middleware.rs b/src/web/middleware.rs index 324c279b..e89c6b15 100644 --- a/src/web/middleware.rs +++ b/src/web/middleware.rs @@ -6,7 +6,6 @@ use actix_web::{ middleware::{Middleware, Response, Started}, FromRequest, HttpRequest, HttpResponse, Result, }; -use chrono::Utc; use futures::{ future::{self, Either}, Future, @@ -18,7 +17,8 @@ use server::ServerState; use web::extractors::{BsoParam, CollectionParam, HawkIdentifier, PreConditionHeader}; /// Default Timestamp used for WeaveTimestamp middleware. -struct DefaultWeaveTimestamp(f64); +#[derive(Default)] +struct DefaultWeaveTimestamp(SyncTimestamp); /// Middleware to set the X-Weave-Timestamp header on all responses. pub struct WeaveTimestamp; @@ -26,18 +26,16 @@ pub struct WeaveTimestamp; impl Middleware for WeaveTimestamp { /// Set the `DefaultWeaveTimestamp` and attach to the `HttpRequest` fn start(&self, req: &HttpRequest) -> Result { - // Get millisecond resolution and convert to seconds - let ts = Utc::now().timestamp_millis() as f64 / 1_000.0; - req.extensions_mut().insert(DefaultWeaveTimestamp(ts)); + req.extensions_mut() + .insert(DefaultWeaveTimestamp::default()); Ok(Started::Done) } /// Method is called when handler returns response, /// but before sending http message to peer. fn response(&self, req: &HttpRequest, mut resp: HttpResponse) -> Result { - let extensions = req.extensions(); - let ts = match extensions.get::() { - Some(ts) => ts, + let ts = match req.extensions().get::() { + Some(ts) => ts.0.as_seconds(), None => return Ok(Response::Done(resp)), }; @@ -58,13 +56,13 @@ impl Middleware for WeaveTimestamp { )).into(); error })?; - if resp_ts > ts.0 { + if resp_ts > ts { resp_ts } else { - ts.0 + ts } } else { - ts.0 + ts }; resp.headers_mut().insert( "x-weave-timestamp", @@ -144,6 +142,9 @@ impl Middleware for DbTransaction { } } +/// The resource in question's Timestamp +pub struct ResourceTimestamp(SyncTimestamp); + #[derive(Debug)] pub struct PreConditionCheck; @@ -170,7 +171,7 @@ impl Middleware for PreConditionCheck { .and_then(move |resource_ts: SyncTimestamp| { // Ensure we stash the extracted resource timestamp on the request in case its // requested elsewhere - req.extensions_mut().insert(resource_ts.clone()); + req.extensions_mut().insert(ResourceTimestamp(resource_ts)); let status = match precondition { PreConditionHeader::IfModifiedSince(header_ts) if resource_ts <= header_ts => { StatusCode::NOT_MODIFIED @@ -195,7 +196,8 @@ impl Middleware for PreConditionCheck { } // See if we already extracted one and use that if possible - if let Some(ts) = req.extensions().get::() { + if let Some(resource_ts) = req.extensions().get::() { + let ts = resource_ts.0; if let Ok(ts_header) = header::HeaderValue::from_str(&ts.as_header()) { resp.headers_mut().insert("X-Last-Modified", ts_header); } @@ -226,6 +228,7 @@ mod tests { use super::*; use actix_web::http; use actix_web::test::TestRequest; + use chrono::Utc; #[test] fn test_no_modified_header() {