Skip to content

Commit 3381e7e

Browse files
Merge pull request #1215 from miwig/cache-static-responses
Cache static responses
2 parents 0b7128a + 3092d6c commit 3381e7e

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ tar = "0.4"
4444
inferno = { version="0.10", default-features = false }
4545
mime = "0.3"
4646
prometheus = "0.12"
47+
uuid = { version = "0.8.2", features = ["v4"] }
4748

4849
[target.'cfg(unix)'.dependencies]
4950
jemallocator = "0.3"

site/src/server.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,23 @@ use brotli::BrotliCompress;
33
use std::collections::HashMap;
44
use std::net::SocketAddr;
55
use std::path::Path;
6+
use std::str::FromStr;
67
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
78
use std::sync::Arc;
89
use std::time::Instant;
910
use std::{fmt, fs, str};
1011

1112
use futures::{future::FutureExt, stream::StreamExt};
12-
use headers::CacheControl;
13-
use headers::{Authorization, ContentType, Header};
13+
use headers::{Authorization, CacheControl, ContentType, ETag, Header, HeaderMapExt, IfNoneMatch};
14+
use http::header::CACHE_CONTROL;
1415
use hyper::StatusCode;
1516
use log::{debug, error, info};
1617
use parking_lot::{Mutex, RwLock};
1718
use ring::hmac;
1819
use rmp_serde;
1920
use serde::de::DeserializeOwned;
2021
use serde::Serialize;
22+
use uuid::Uuid;
2123

2224
pub use crate::api::{
2325
self, bootstrap, comparison, dashboard, github, graphs, info, self_profile, self_profile_raw,
@@ -334,7 +336,7 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
334336
None
335337
};
336338

337-
if let Some(response) = handle_fs_path(path) {
339+
if let Some(response) = handle_fs_path(&req, path) {
338340
return Ok(response);
339341
}
340342

@@ -561,8 +563,12 @@ where
561563
}
562564
}
563565

566+
lazy_static::lazy_static! {
567+
static ref VERSION_UUID: Uuid = Uuid::new_v4(); // random UUID used as ETag for cache revalidation
568+
}
569+
564570
/// Handle the case where the path is to a static file
565-
fn handle_fs_path(path: &str) -> Option<http::Response<hyper::Body>> {
571+
fn handle_fs_path(req: &Request, path: &str) -> Option<http::Response<hyper::Body>> {
566572
let fs_path = format!(
567573
"site/static{}",
568574
match path {
@@ -579,8 +585,19 @@ fn handle_fs_path(path: &str) -> Option<http::Response<hyper::Body>> {
579585
return None;
580586
}
581587

588+
let etag = ETag::from_str(&*format!(r#""{}""#, *VERSION_UUID)).unwrap();
589+
let mut response = http::Response::builder()
590+
.header_typed(etag.clone())
591+
.header(CACHE_CONTROL, "max-age=60, stale-while-revalidate=86400"); // tell client to use cached response for one day, but revalidate in background if older than one minute
592+
593+
let if_none_match = req.headers().typed_get::<IfNoneMatch>();
594+
if let Some(if_none_match) = if_none_match {
595+
if !if_none_match.precondition_passes(&etag) {
596+
return Some(not_modified(response)); // tell client that the resource was not modified and to use cached response
597+
}
598+
}
599+
582600
let source = fs::read(&fs_path).unwrap();
583-
let mut response = http::Response::builder();
584601
let p = Path::new(&fs_path);
585602
match p.extension().and_then(|x| x.to_str()) {
586603
Some("html") => response = response.header_typed(ContentType::html()),
@@ -594,6 +611,13 @@ fn handle_fs_path(path: &str) -> Option<http::Response<hyper::Body>> {
594611
Some(response.body(hyper::Body::from(source)).unwrap())
595612
}
596613

614+
fn not_modified(response: http::response::Builder) -> http::Response<hyper::Body> {
615+
response
616+
.status(StatusCode::NOT_MODIFIED)
617+
.body(hyper::Body::empty())
618+
.unwrap()
619+
}
620+
597621
fn not_found() -> http::Response<hyper::Body> {
598622
http::Response::builder()
599623
.header_typed(ContentType::html())

0 commit comments

Comments
 (0)