index : static-web-server.git

ascending towards madness

author Jose Quintana <joseluisquintana20@gmail.com> 2021-05-09 14:37:54.0 +00:00:00
committer Jose Quintana <joseluisquintana20@gmail.com> 2021-05-09 14:37:54.0 +00:00:00
commit
a8b0af6295d0b966ff58c6e1ec68e8cd0ece0211 [patch]
tree
c6ac557541be7e45a402ebf4393007c9e8244f25
parent
dc74a9a14329191cbd710e83bfab571cf6d63e42
download
a8b0af6295d0b966ff58c6e1ec68e8cd0ece0211.tar.gz

feat: default cache-control headers for web assets



Diff

 src/control_headers.rs | 41 +++++++++++++++++++++++++++++++++++++++++
 src/handler.rs         | 13 +++++++++++--
 src/lib.rs             |  1 +
 3 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/src/control_headers.rs b/src/control_headers.rs
new file mode 100644
index 0000000..4641583
--- /dev/null
+++ b/src/control_headers.rs
@@ -0,0 +1,41 @@
// An arbitrary `Cache-Control` headers functionality for incoming requests based on a set of file types.
// Note: Since it's a ad-hoc feature it could be subject to change.
// See https://github.com/joseluisq/static-web-server/issues/30

use headers::{CacheControl, HeaderMapExt};
use hyper::{Body, Response};

const CACHE_EXT_ONE_HOUR: [&str; 4] = ["atom", "json", "rss", "xml"];
const CACHE_EXT_ONE_YEAR: [&str; 30] = [
    "bmp", "bz2", "css", "doc", "gif", "gz", "htc", "ico", "jpeg", "jpg", "js", "map", "mjs",
    "mp3", "mp4", "ogg", "ogv", "pdf", "png", "rar", "rtf", "tar", "tgz", "wav", "weba", "webm",
    "webp", "woff", "woff2", "zip",
];

/// It appends a `Cache-Control` header to a response if that one is part of a set of file types.
pub fn with_cache_control(ext: &str, resp: &mut Response<Body>) {
    // Default max-age value in seconds (one day)
    let mut max_age = 60 * 60 * 24_u64;

    if CACHE_EXT_ONE_HOUR
        .iter()
        .any(|x| ext.ends_with(&[".", *x].concat()))
    {
        max_age = 60 * 60;
    } else if CACHE_EXT_ONE_YEAR
        .iter()
        .any(|x| ext.ends_with(&[".", *x].concat()))
    {
        max_age = 60 * 60 * 24 * 365;
    }

    let cache_control = CacheControl::new()
        .with_public()
        .with_max_age(duration_from_secs(max_age));
    resp.headers_mut().typed_insert(cache_control);
}

/// It caps a duration value at ~136 years.
fn duration_from_secs(secs: u64) -> std::time::Duration {
    std::time::Duration::from_secs(std::cmp::min(secs, u32::MAX as u64))
}
diff --git a/src/handler.rs b/src/handler.rs
index 7ab7c2d..ad65b19 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -1,7 +1,7 @@
use hyper::{Body, Request, Response};
use std::path::Path;

use crate::{compression, static_files};
use crate::{compression, control_headers, static_files};
use crate::{error::Result, error_page};

/// Main server request handler.
@@ -10,7 +10,16 @@ pub async fn handle_request(base: &Path, req: &Request<Body>) -> Result<Response
    let method = req.method();

    match static_files::handle_request(method, headers, base, req.uri().path()).await {
        Ok(resp) => compression::auto(method, headers, resp),
        Ok(resp) => {
            // Compression on demand based on`Accept-Encoding` header
            let mut resp = compression::auto(method, headers, resp)?;

            // Append `Cache-Control` headers for web assets
            let ext = req.uri().path().to_lowercase();
            control_headers::with_cache_control(&ext, &mut resp);

            Ok(resp)
        }
        Err(status) => error_page::get_error_response(method, &status),
    }
}
diff --git a/src/lib.rs b/src/lib.rs
index 3ccd4e5..fa591ab 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,6 +5,7 @@ extern crate anyhow;

pub mod compression;
pub mod config;
pub mod control_headers;
pub mod error_page;
pub mod handler;
pub mod helpers;