Skip to content
This repository has been archived by the owner on Apr 20, 2023. It is now read-only.

Allow caching requests to endpoints that serve immutable content #195

Open
rudolfs opened this issue Sep 9, 2022 · 1 comment
Open

Allow caching requests to endpoints that serve immutable content #195

rudolfs opened this issue Sep 9, 2022 · 1 comment
Assignees

Comments

@rudolfs
Copy link
Member

rudolfs commented Sep 9, 2022

We could potentially allow browsers caching the following endpoint responses indefinitely:

/v1/projects/:project/commits/:sha
/v1/projects/:project/tree/:sha/*path
/v1/projects/:project/blob/:sha/*path
/v1/projects/:project/readme/:sha

Because the content is addressed by a SHA and shouldn't change (unless there's a collision).

But unfortunately the responses contain a lastCommit attribute which can vary. Looking at the front-end code, we only use this attribute when querying the /v1/projects/:project/blob/:sha/*path endpoint. And even that could be fetched in a separate request.

Therefore I propose we remove the lastCommit attribute from all the requests and create a separate endpoint for getting the lastCommit info for a blob and update the front-end. And finally add caching headers to all the endpoints.

@rudolfs
Copy link
Member Author

rudolfs commented Sep 9, 2022

Here's a snippet for adding the cache headers:

diff --git a/http-api/Cargo.toml b/http-api/Cargo.toml
index 006cd1f..50f5794 100644
--- a/http-api/Cargo.toml
+++ b/http-api/Cargo.toml
@@ -34,7 +34,7 @@ chrono = { version = "0.4.19", features = ["serde"] }
 axum = { version = "0.5.3", default-features = false, features = ["json", "headers", "query"] }
 axum-server = { version = "0.3", default-features = false, features = ["tls-rustls"] }
 hyper = { version ="0.14.17", default-features = false, features = ["server"] }
-tower-http = { version = "0.3.0", default-features = false, features = ["trace", "cors"] }
+tower-http = { version = "0.3.0", default-features = false, features = ["trace", "cors", "set-header"] }
 
 [dev-dependencies]
 tower = { version = "0.4", features = ["util"] }
diff --git a/http-api/src/v1/projects.rs b/http-api/src/v1/projects.rs
index c600c91..25037a7 100644
--- a/http-api/src/v1/projects.rs
+++ b/http-api/src/v1/projects.rs
@@ -2,6 +2,8 @@ use std::collections::HashMap;
 use std::convert::{TryFrom, TryInto};
 use std::str::FromStr;
 
+use axum::handler::Handler;
+use axum::http::{header, HeaderValue};
 use axum::response::IntoResponse;
 use axum::routing::get;
 use axum::{Extension, Json, Router};
@@ -9,6 +11,7 @@ use hyper::StatusCode;
 use librad::identities::Project;
 use serde::Deserialize;
 use serde_json::json;
+use tower_http::set_header::SetResponseHeaderLayer;
 
 use librad::collaborative_objects::ObjectId;
 use librad::git::identities::{self, SomeIdentity};
@@ -30,18 +33,53 @@ use crate::commit::{Commit, CommitContext, CommitTeaser, CommitsQueryString, Com
 use crate::project::{self, Info};
 use crate::{get_head_commit, Context, Error};
 
+const CACHE_STATIC: &str = "public, max-age=31536000, immutable";
+const CACHE_1_HOUR: &str = "public, max-age=3600, must-revalidate";
+
 pub fn router(ctx: Context) -> Router {
     Router::new()
         .route("/projects", get(project_root_handler))
         .route("/projects/:project", get(project_alias_or_urn_handler))
         .route("/projects/:project/commits", get(history_handler))
-        .route("/projects/:project/commits/:sha", get(commit_handler))
-        .route("/projects/:project/activity", get(activity_handler))
-        .route("/projects/:project/tree/:sha/*path", get(tree_handler))
+        .route(
+            "/projects/:project/commits/:sha",
+            get(commit_handler.layer(SetResponseHeaderLayer::if_not_present(
+                header::CACHE_CONTROL,
+                HeaderValue::from_static(CACHE_STATIC),
+            ))),
+        )
+        .route(
+            "/projects/:project/activity",
+            get(
+                activity_handler.layer(SetResponseHeaderLayer::if_not_present(
+                    header::CACHE_CONTROL,
+                    HeaderValue::from_static(CACHE_1_HOUR),
+                )),
+            ),
+        )
+        .route(
+            "/projects/:project/tree/:sha/*path",
+            get(tree_handler.layer(SetResponseHeaderLayer::if_not_present(
+                header::CACHE_CONTROL,
+                HeaderValue::from_static(CACHE_STATIC),
+            ))),
+        )
         .route("/projects/:project/remotes", get(remotes_handler))
         .route("/projects/:project/remotes/:peer", get(remote_handler))
-        .route("/projects/:project/blob/:sha/*path", get(blob_handler))
-        .route("/projects/:project/readme/:sha", get(readme_handler))
+        .route(
+            "/projects/:project/blob/:sha/*path",
+            get(blob_handler.layer(SetResponseHeaderLayer::if_not_present(
+                header::CACHE_CONTROL,
+                HeaderValue::from_static(CACHE_STATIC),
+            ))),
+        )
+        .route(
+            "/projects/:project/readme/:sha",
+            get(readme_handler.layer(SetResponseHeaderLayer::if_not_present(
+                header::CACHE_CONTROL,
+                HeaderValue::from_static(CACHE_STATIC),
+            ))),
+        )
         .route("/projects/:project/patches", get(patches_handler))
         .route("/projects/:project/patches/:id", get(patch_handler))
         .route("/projects/:project/issues", get(issues_handler))

@sebastinez sebastinez self-assigned this Nov 7, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants