Skip to content

Commit

Permalink
Add Cache-Control to rustdoc pages
Browse files Browse the repository at this point in the history
For /latest/ URLs, set max-age=0. For versioned URLs max-age=10 minutes
and stale-while-revalidate=2 months.

The idea behind this is that versioned URLs change mostly in minor ways
- the "Go to latest" link at the top, and the list of versions in the
crate menu. And setting a long cache time (either via max-age or via
stale-while-revalidate) allows pages to be loaded even while offline.

We could probably apply a long stale-while-revalidate to /latest/ URLs
as well, but this is more likely to have a user-noticeable impact, and
the /latest/ URLs are relatively new so we don't want to create any
confusing interactions.
  • Loading branch information
jsha committed Dec 1, 2021
1 parent 0607ed2 commit c55705b
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions src/web/rustdoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use crate::{
Config, Metrics, Storage,
};
use anyhow::{anyhow, Context};
use iron::url::percent_encoding::percent_decode;
use iron::{
headers::{CacheControl, CacheDirective},
url::percent_encoding::percent_decode,
};
use iron::{
headers::{Expires, HttpDate},
modifiers::Redirect,
Expand Down Expand Up @@ -198,7 +201,11 @@ struct RustdocPage {
latest_version: String,
target: String,
inner_path: String,
// true if we are displaying the latest version of the crate, regardless
// of whether the URL specifies a version number or the string "latest."
is_latest_version: bool,
// true if the URL specifies a version using the string "latest."
is_latest_url: bool,
is_prerelease: bool,
krate: CrateDetails,
metadata: MetaData,
Expand All @@ -224,6 +231,7 @@ impl RustdocPage {
.get::<crate::Metrics>()
.expect("missing Metrics from the request extensions");

let is_latest_url = self.is_latest_url;
// Build the page of documentation
let ctx = ctry!(req, tera::Context::from_serialize(self));
// Extract the head and body of the rustdoc file so that we can insert it into our own html
Expand All @@ -245,7 +253,19 @@ impl RustdocPage {

let mut response = Response::with((Status::Ok, html));
response.headers.set(ContentType::html());

if is_latest_url {
response
.headers
.set(CacheControl(vec![CacheDirective::MaxAge(0)]));
} else {
response.headers.set(CacheControl(vec![
CacheDirective::Extension(
"stale-while-revalidate".to_string(),
Some("2592000".to_string()), // sixty days
),
CacheDirective::MaxAge(600u32), // ten minutes
]));
}
Ok(response)
}
}
Expand Down Expand Up @@ -492,6 +512,7 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
target,
inner_path,
is_latest_version,
is_latest_url: version_or_latest == "latest",
is_prerelease,
metadata: krate.metadata.clone(),
krate,
Expand Down Expand Up @@ -831,6 +852,28 @@ mod test {
})
}

#[test]
fn cache_headers() {
wrapper(|env| {
env.fake_release()
.name("dummy")
.version("0.1.0")
.archive_storage(true)
.rustdoc_file("dummy/index.html")
.create()?;

let resp = env.frontend().get("/dummy/latest/dummy/").send()?;
assert_eq!(resp.headers().get("Cache-Control").unwrap(), &"max-age=0");

let resp = env.frontend().get("/dummy/0.1.0/dummy/").send()?;
assert_eq!(
resp.headers().get("Cache-Control").unwrap(),
&"stale-while-revalidate=2592000, max-age=600"
);
Ok(())
})
}

#[test_case(true)]
#[test_case(false)]
fn go_to_latest_version(archive_storage: bool) {
Expand Down

0 comments on commit c55705b

Please sign in to comment.