From 217f3bf28a5e48fb7d344a76a7d553901494f0db Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 15 Sep 2021 22:32:35 +0000 Subject: [PATCH] Add cache-control headers to API responses --- sync-server/src/api/get_child_version.rs | 1 - .../src/bin/taskchampion-sync-server.rs | 34 +++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/sync-server/src/api/get_child_version.rs b/sync-server/src/api/get_child_version.rs index f5d67184e..d67cde8f2 100644 --- a/sync-server/src/api/get_child_version.rs +++ b/sync-server/src/api/get_child_version.rs @@ -44,7 +44,6 @@ pub(crate) async fn service( #[cfg(test)] mod test { - use crate::api::ServerState; use crate::storage::{InMemoryStorage, Storage}; use crate::Server; use actix_web::{http::StatusCode, test, App}; diff --git a/sync-server/src/bin/taskchampion-sync-server.rs b/sync-server/src/bin/taskchampion-sync-server.rs index 88f0bb180..30056351e 100644 --- a/sync-server/src/bin/taskchampion-sync-server.rs +++ b/sync-server/src/bin/taskchampion-sync-server.rs @@ -1,10 +1,21 @@ #![deny(clippy::all)] -use actix_web::{middleware::Logger, App, HttpServer}; +use actix_web::{middleware, middleware::Logger, App, HttpServer}; use clap::Arg; use taskchampion_sync_server::storage::SqliteStorage; use taskchampion_sync_server::Server; +// The `.wrap` method returns an opaque type, meaning that we can't easily return it from +// functions. So, we must apply these default headers when the app is created, which occurs both +// in `main` and in the tests. To check that those are both doing precisely the same thing, we use +// a macro. This is ugly, and will go away when actix-web is no longer the framework in use. +macro_rules! cache_control_headers { + ($wrapped:expr) => { + $wrapped + .wrap(middleware::DefaultHeaders::new().header("Cache-Control", "no-store, max-age=0")) + }; +} + #[actix_web::main] async fn main() -> anyhow::Result<()> { env_logger::init(); @@ -39,10 +50,14 @@ async fn main() -> anyhow::Result<()> { let server = Server::new(Box::new(SqliteStorage::new(data_dir)?)); log::warn!("Serving on port {}", port); - HttpServer::new(move || App::new().wrap(Logger::default()).service(server.service())) - .bind(format!("0.0.0.0:{}", port))? - .run() - .await?; + HttpServer::new(move || { + cache_control_headers!(App::new()) + .wrap(Logger::default()) + .service(server.service()) + }) + .bind(format!("0.0.0.0:{}", port))? + .run() + .await?; Ok(()) } @@ -50,15 +65,20 @@ async fn main() -> anyhow::Result<()> { mod test { use super::*; use actix_web::{test, App}; - use taskchampion_sync_server::storage::{InMemoryStorage, Storage}; + use taskchampion_sync_server::storage::InMemoryStorage; #[actix_rt::test] async fn test_index_get() { let server = Server::new(Box::new(InMemoryStorage::new())); - let mut app = test::init_service(App::new().service(server.service())).await; + let app = cache_control_headers!(App::new()).service(server.service()); + let mut app = test::init_service(app).await; let req = test::TestRequest::get().uri("/").to_request(); let resp = test::call_service(&mut app, req).await; assert!(resp.status().is_success()); + assert_eq!( + resp.headers().get("Cache-Control").unwrap(), + &"no-store, max-age=0".to_string() + ) } }