httpd: Mark immutable response as such with Cache-Control header
For responses to requests for content that is content-addressed with Git object IDs and consequently immutable, we add a Cache-Control HTTP header that marks the responses as public, immutable and instructs clients to cache the response for up to 7 days.
This commit is contained in:
parent
1c4a2cd167
commit
e52ce483a1
|
|
@ -28,7 +28,7 @@ use radicle_surf::{diff, Glob, Oid, Repository};
|
||||||
use crate::api::error::Error;
|
use crate::api::error::Error;
|
||||||
use crate::api::project::Info;
|
use crate::api::project::Info;
|
||||||
use crate::api::{self, announce_refs, CobsQuery, Context, PaginationQuery, ProjectQuery};
|
use crate::api::{self, announce_refs, CobsQuery, Context, PaginationQuery, ProjectQuery};
|
||||||
use crate::axum_extra::{Path, Query};
|
use crate::axum_extra::{immutable_response, Path, Query};
|
||||||
|
|
||||||
const CACHE_1_HOUR: &str = "public, max-age=3600, must-revalidate";
|
const CACHE_1_HOUR: &str = "public, max-age=3600, must-revalidate";
|
||||||
const MAX_BODY_LIMIT: usize = 4_194_304;
|
const MAX_BODY_LIMIT: usize = 4_194_304;
|
||||||
|
|
@ -181,7 +181,7 @@ pub struct CommitsQueryString {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get project commit range.
|
/// Get project commit range.
|
||||||
/// `GET /projects/:project/commits?since=<sha>`
|
/// `GET /projects/:project/commits?parent=<sha>`
|
||||||
async fn history_handler(
|
async fn history_handler(
|
||||||
State(ctx): State<Context>,
|
State(ctx): State<Context>,
|
||||||
Path(project): Path<RepoId>,
|
Path(project): Path<RepoId>,
|
||||||
|
|
@ -195,6 +195,11 @@ async fn history_handler(
|
||||||
per_page,
|
per_page,
|
||||||
} = qs;
|
} = qs;
|
||||||
|
|
||||||
|
// If the parent commit is provided, the response depends only on the query
|
||||||
|
// string and not on the state of the repository. This means we can instruct
|
||||||
|
// the caches to treat the response as immutable.
|
||||||
|
let is_immutable = parent.is_some();
|
||||||
|
|
||||||
let sha = match parent {
|
let sha = match parent {
|
||||||
Some(commit) => commit,
|
Some(commit) => commit,
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -258,7 +263,11 @@ async fn history_handler(
|
||||||
"stats": repo.stats_from(&sha)?,
|
"stats": repo.stats_from(&sha)?,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok::<_, Error>((StatusCode::OK, Json(response)))
|
if is_immutable {
|
||||||
|
Ok::<_, Error>(immutable_response(response).into_response())
|
||||||
|
} else {
|
||||||
|
Ok::<_, Error>(Json(response).into_response())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get project commit.
|
/// Get project commit.
|
||||||
|
|
@ -318,13 +327,13 @@ async fn commit_handler(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let response = json!({
|
let response: serde_json::Value = json!({
|
||||||
"commit": api::json::commit(&commit),
|
"commit": api::json::commit(&commit),
|
||||||
"diff": diff,
|
"diff": diff,
|
||||||
"files": files,
|
"files": files,
|
||||||
"branches": branches
|
"branches": branches
|
||||||
});
|
});
|
||||||
Ok::<_, Error>(Json(response))
|
Ok::<_, Error>(immutable_response(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get diff between two commits
|
/// Get diff between two commits
|
||||||
|
|
@ -391,7 +400,7 @@ async fn diff_handler(
|
||||||
|
|
||||||
let response = json!({ "diff": diff, "files": files, "commits": commits });
|
let response = json!({ "diff": diff, "files": files, "commits": commits });
|
||||||
|
|
||||||
Ok::<_, Error>(Json(response))
|
Ok::<_, Error>(immutable_response(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get project activity for the past year.
|
/// Get project activity for the past year.
|
||||||
|
|
@ -439,7 +448,7 @@ async fn tree_handler(
|
||||||
if let Some(ref cache) = ctx.cache {
|
if let Some(ref cache) = ctx.cache {
|
||||||
let cache = &mut cache.tree.lock().await;
|
let cache = &mut cache.tree.lock().await;
|
||||||
if let Some(response) = cache.get(&(project, sha, path.clone())) {
|
if let Some(response) = cache.get(&(project, sha, path.clone())) {
|
||||||
return Ok::<_, Error>(Json(response.clone()));
|
return Ok::<_, Error>(immutable_response(response.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -454,7 +463,7 @@ async fn tree_handler(
|
||||||
cache.put((project, sha, path.clone()), response.clone());
|
cache.put((project, sha, path.clone()), response.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok::<_, Error>(Json(response))
|
Ok::<_, Error>(immutable_response(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all project remotes.
|
/// Get all project remotes.
|
||||||
|
|
@ -566,7 +575,7 @@ async fn readme_handler(
|
||||||
{
|
{
|
||||||
if let Ok(blob) = repo.blob(sha, &path) {
|
if let Ok(blob) = repo.blob(sha, &path) {
|
||||||
let response = api::json::blob(&blob, &path);
|
let response = api::json::blob(&blob, &path);
|
||||||
return Ok::<_, Error>(Json(response));
|
return Ok::<_, Error>(immutable_response(response));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use axum::extract::path::ErrorKind;
|
||||||
use axum::extract::rejection::{PathRejection, QueryRejection};
|
use axum::extract::rejection::{PathRejection, QueryRejection};
|
||||||
use axum::extract::FromRequestParts;
|
use axum::extract::FromRequestParts;
|
||||||
use axum::http::request::Parts;
|
use axum::http::request::Parts;
|
||||||
use axum::http::StatusCode;
|
use axum::http::{header, StatusCode};
|
||||||
|
use axum::response::IntoResponse;
|
||||||
use axum::{async_trait, Json};
|
use axum::{async_trait, Json};
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
@ -87,3 +88,12 @@ pub struct Error {
|
||||||
success: bool,
|
success: bool,
|
||||||
error: String,
|
error: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a Cache-Control header that marks the response as immutable and
|
||||||
|
/// instructs clients to cache the response for 7 days.
|
||||||
|
pub fn immutable_response(data: impl serde::Serialize) -> impl IntoResponse {
|
||||||
|
(
|
||||||
|
[(header::CACHE_CONTROL, "public, max-age=604800, immutable")],
|
||||||
|
Json(data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue