Skip to content

Commit 95c34a7

Browse files
jshasyphar
authored andcommitted
Add Cache-Control to rustdoc pages
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.
1 parent a074c8a commit 95c34a7

File tree

1 file changed

+45
-2
lines changed

1 file changed

+45
-2
lines changed

src/web/rustdoc.rs

+45-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ use crate::{
1111
Config, Metrics, Storage,
1212
};
1313
use anyhow::{anyhow, Context};
14-
use iron::url::percent_encoding::percent_decode;
14+
use iron::{
15+
headers::{CacheControl, CacheDirective},
16+
url::percent_encoding::percent_decode,
17+
};
1518
use iron::{
1619
headers::{CacheControl, CacheDirective, Expires, HttpDate},
1720
modifiers::Redirect,
@@ -218,7 +221,11 @@ struct RustdocPage {
218221
latest_version: String,
219222
target: String,
220223
inner_path: String,
224+
// true if we are displaying the latest version of the crate, regardless
225+
// of whether the URL specifies a version number or the string "latest."
221226
is_latest_version: bool,
227+
// true if the URL specifies a version using the string "latest."
228+
is_latest_url: bool,
222229
is_prerelease: bool,
223230
krate: CrateDetails,
224231
metadata: MetaData,
@@ -244,6 +251,7 @@ impl RustdocPage {
244251
.get::<crate::Metrics>()
245252
.expect("missing Metrics from the request extensions");
246253

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

266274
let mut response = Response::with((Status::Ok, html));
267275
response.headers.set(ContentType::html());
268-
276+
if is_latest_url {
277+
response
278+
.headers
279+
.set(CacheControl(vec![CacheDirective::MaxAge(0)]));
280+
} else {
281+
response.headers.set(CacheControl(vec![
282+
CacheDirective::Extension(
283+
"stale-while-revalidate".to_string(),
284+
Some("2592000".to_string()), // sixty days
285+
),
286+
CacheDirective::MaxAge(600u32), // ten minutes
287+
]));
288+
}
269289
Ok(response)
270290
}
271291
}
@@ -533,6 +553,7 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
533553
target,
534554
inner_path,
535555
is_latest_version,
556+
is_latest_url: version_or_latest == "latest",
536557
is_prerelease,
537558
metadata: krate.metadata.clone(),
538559
krate,
@@ -871,6 +892,28 @@ mod test {
871892
})
872893
}
873894

895+
#[test]
896+
fn cache_headers() {
897+
wrapper(|env| {
898+
env.fake_release()
899+
.name("dummy")
900+
.version("0.1.0")
901+
.archive_storage(true)
902+
.rustdoc_file("dummy/index.html")
903+
.create()?;
904+
905+
let resp = env.frontend().get("/dummy/latest/dummy/").send()?;
906+
assert_eq!(resp.headers().get("Cache-Control").unwrap(), &"max-age=0");
907+
908+
let resp = env.frontend().get("/dummy/0.1.0/dummy/").send()?;
909+
assert_eq!(
910+
resp.headers().get("Cache-Control").unwrap(),
911+
&"stale-while-revalidate=2592000, max-age=600"
912+
);
913+
Ok(())
914+
})
915+
}
916+
874917
#[test_case(true)]
875918
#[test_case(false)]
876919
fn go_to_latest_version(archive_storage: bool) {

0 commit comments

Comments
 (0)