diff options
author | metamuffin <metamuffin@disroot.org> | 2024-04-30 01:48:46 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-04-30 01:48:46 +0200 |
commit | f57e6a8e8977d1acdfafeda7b1444cb155730894 (patch) | |
tree | e5f076c02d5760a5ba9e3a34e8d70d8cc0044179 | |
parent | 398551e20f28c136d9e3be2ccb33e31ffe50cb91 (diff) | |
download | gnix-f57e6a8e8977d1acdfafeda7b1444cb155730894.tar gnix-f57e6a8e8977d1acdfafeda7b1444cb155730894.tar.bz2 gnix-f57e6a8e8977d1acdfafeda7b1444cb155730894.tar.zst |
implement if-not-modified
-rw-r--r-- | Cargo.lock | 12 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | src/config.rs | 7 | ||||
-rw-r--r-- | src/filters/files.rs | 77 |
4 files changed, 83 insertions, 16 deletions
@@ -317,7 +317,9 @@ dependencies = [ "futures", "futures-util", "headers", + "hex", "http-body-util", + "httpdate", "humansize", "hyper", "hyper-util", @@ -401,6 +403,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] name = "http" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -442,9 +450,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humansize" @@ -12,6 +12,8 @@ http-body-util = "0.1.0-rc.3" headers = "0.3.8" percent-encoding = "2.3.0" base64 = "0.21.5" +httpdate = "1.0.3" +hex = "0.4.3" # TLS rustls-pemfile = "1.0.3" @@ -39,7 +41,6 @@ markup = "0.13.1" humansize = "2.1.3" mime_guess = "2.0.4" - bytes = "1.4.0" anyhow = "1.0.75" thiserror = "1.0.47" diff --git a/src/config.rs b/src/config.rs index bc4369d..700c4ed 100644 --- a/src/config.rs +++ b/src/config.rs @@ -96,6 +96,13 @@ pub struct FileserverConfig { pub root: PathBuf, #[serde(default)] pub index: bool, + #[serde(default = "return_true")] + pub last_modified: bool, + #[serde(default = "return_true")] + pub etag: bool, +} +fn return_true() -> bool { + true } // try deser Vec<T> but fall back to deser T and putting that in Vec diff --git a/src/filters/files.rs b/src/filters/files.rs index 733d045..59de2f3 100644 --- a/src/filters/files.rs +++ b/src/filters/files.rs @@ -1,7 +1,7 @@ use crate::{config::FileserverConfig, ServiceError}; use bytes::{Bytes, BytesMut}; use futures_util::{future, future::Either, ready, stream, FutureExt, Stream, StreamExt}; -use headers::{AcceptRanges, ContentLength, ContentRange, ContentType, HeaderMapExt}; +use headers::{AcceptRanges, ContentLength, ContentRange, ContentType, HeaderMapExt, LastModified}; use http_body_util::{combinators::BoxBody, BodyExt, StreamBody}; use humansize::FormatSizeOptions; use hyper::{ @@ -78,30 +78,73 @@ pub async fn serve_files( } } + let modified = metadata.modified()?; + + let not_modified = if config.last_modified { + req.headers() + .typed_get::<headers::IfModifiedSince>() + .map(|if_modified_since| { + Ok::<_, ServiceError>(!if_modified_since.is_modified(modified)) + }) + .transpose()? + .unwrap_or_default() + } else { + false + }; + + // let etag = ETag::from_str(&calc_etag(modified)).map_err(|_| ServiceError::Other)?; + // let etag_matches = if config.etag { + // req.headers() + // .typed_get::<headers::IfNoneMatch>() + // .map(|if_none_match| if_none_match.precondition_passes(&etag)) + // .unwrap_or_default() + // } else { + // false + // }; + let range = req.headers().typed_get::<headers::Range>(); let range = bytes_range(range, metadata.len())?; debug!("sending file {path:?}"); let file = File::open(path.clone()).await?; - let mut r = Response::new(BoxBody::new(StreamBody::new( - StreamBody::new(file_stream(file, 4096, range.clone())) - .map(|e| e.map(|e| Frame::data(e)).map_err(ServiceError::Io)), - ))); + // let skip_body = not_modified || etag_matches; + let skip_body = not_modified; + let mut r = if skip_body { + Response::new("".to_string()).map(|b| b.map_err(|e| match e {}).boxed()) + } else { + Response::new(BoxBody::new(StreamBody::new( + StreamBody::new(file_stream(file, 4096, range.clone())) + .map(|e| e.map(|e| Frame::data(e)).map_err(ServiceError::Io)), + ))) + }; - if range.end - range.start != metadata.len() { - *r.status_mut() = StatusCode::PARTIAL_CONTENT; - r.headers_mut().typed_insert( - ContentRange::bytes(range.clone(), metadata.len()).expect("valid ContentRange"), - ); + if !skip_body { + if range.end - range.start != metadata.len() { + *r.status_mut() = StatusCode::PARTIAL_CONTENT; + r.headers_mut().typed_insert( + ContentRange::bytes(range.clone(), metadata.len()).expect("valid ContentRange"), + ); + } + } + // if not_modified || etag_matches { + if not_modified { + *r.status_mut() = StatusCode::NOT_MODIFIED; } - let mime = mime_guess::from_path(path).first_or_octet_stream(); - + r.headers_mut().typed_insert(AcceptRanges::bytes()); r.headers_mut() .typed_insert(ContentLength(range.end - range.start)); + + let mime = mime_guess::from_path(path).first_or_octet_stream(); r.headers_mut().typed_insert(ContentType::from(mime)); - r.headers_mut().typed_insert(AcceptRanges::bytes()); + + // if config.etag { + // r.headers_mut().typed_insert(etag); + // } + if config.last_modified { + r.headers_mut().typed_insert(LastModified::from(modified)); + } Ok(r) } @@ -276,3 +319,11 @@ markup::define! { } } } + +// fn calc_etag(s: SystemTime) -> String { +// // TODO: make this not change after server restart but still unguessable +// let mut hasher = DefaultHasher::new(); +// s.hash(&mut hasher); +// let hash = hasher.finish(); +// hex::encode(hash.to_le_bytes()) +// } |