summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-04-30 01:48:46 +0200
committermetamuffin <metamuffin@disroot.org>2024-04-30 01:48:46 +0200
commitf57e6a8e8977d1acdfafeda7b1444cb155730894 (patch)
treee5f076c02d5760a5ba9e3a34e8d70d8cc0044179
parent398551e20f28c136d9e3be2ccb33e31ffe50cb91 (diff)
downloadgnix-f57e6a8e8977d1acdfafeda7b1444cb155730894.tar
gnix-f57e6a8e8977d1acdfafeda7b1444cb155730894.tar.bz2
gnix-f57e6a8e8977d1acdfafeda7b1444cb155730894.tar.zst
implement if-not-modified
-rw-r--r--Cargo.lock12
-rw-r--r--Cargo.toml3
-rw-r--r--src/config.rs7
-rw-r--r--src/filters/files.rs77
4 files changed, 83 insertions, 16 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ad78b2c..30c2268 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 565147b..7c4b32a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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())
+// }