aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrinity Pointard <trinity.pointard@gmail.com>2021-03-18 15:46:33 +0100
committerTrinity Pointard <trinity.pointard@gmail.com>2021-03-18 15:46:33 +0100
commitb4c903371c3812851befd1af8f01419fe18efcf3 (patch)
tree896ef4e1729e74bf9ed4068c7121a37d0353ba4d
parent797cda1c33d0b908492d2c08ece1c51c9b10f36d (diff)
downloadgarage-b4c903371c3812851befd1af8f01419fe18efcf3.tar.gz
garage-b4c903371c3812851befd1af8f01419fe18efcf3.zip
add support for caching headers
-rw-r--r--src/api/api_server.rs2
-rw-r--r--src/api/s3_get.rs45
-rw-r--r--src/web/web_server.rs2
3 files changed, 46 insertions, 3 deletions
diff --git a/src/api/api_server.rs b/src/api/api_server.rs
index bc98686d..2feb0e3a 100644
--- a/src/api/api_server.rs
+++ b/src/api/api_server.rs
@@ -101,7 +101,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
match req.method() {
&Method::HEAD => {
// HeadObject query
- Ok(handle_head(garage, &bucket, &key).await?)
+ Ok(handle_head(garage, &req, &bucket, &key).await?)
}
&Method::GET => {
// GetObject query
diff --git a/src/api/s3_get.rs b/src/api/s3_get.rs
index 22a55b55..0e3caad6 100644
--- a/src/api/s3_get.rs
+++ b/src/api/s3_get.rs
@@ -40,8 +40,43 @@ fn object_headers(
resp
}
+fn try_answer_cached(
+ version: &ObjectVersion,
+ version_meta: &ObjectVersionMeta,
+ req: &Request<Body>,
+) -> Option<Response<Body>> {
+ let cached = if let Some(none_match) = req.headers().get(http::header::IF_NONE_MATCH) {
+ let none_match = none_match.to_str().ok()?;
+ let expected = format!("\"{}\"", version_meta.etag);
+ let found = none_match
+ .split(',')
+ .map(str::trim)
+ .any(|etag| etag == expected || etag == "\"*\"");
+ found
+ } else if let Some(modified_since) = req.headers().get(http::header::IF_MODIFIED_SINCE) {
+ let modified_since = modified_since.to_str().ok()?;
+ let client_date = httpdate::parse_http_date(modified_since).ok()?;
+ let server_date = UNIX_EPOCH + Duration::from_millis(version.timestamp);
+ client_date >= server_date
+ } else {
+ false
+ };
+
+ if cached {
+ Some(
+ Response::builder()
+ .status(StatusCode::NOT_MODIFIED)
+ .body(Body::empty())
+ .unwrap(),
+ )
+ } else {
+ None
+ }
+}
+
pub async fn handle_head(
garage: Arc<Garage>,
+ req: &Request<Body>,
bucket: &str,
key: &str,
) -> Result<Response<Body>, Error> {
@@ -65,7 +100,11 @@ pub async fn handle_head(
_ => unreachable!(),
};
- let body: Body = Body::from(vec![]);
+ if let Some(cached) = try_answer_cached(&version, version_meta, req) {
+ return Ok(cached);
+ }
+
+ let body: Body = Body::empty();
let response = object_headers(&version, version_meta)
.header("Content-Length", format!("{}", version_meta.size))
.status(StatusCode::OK)
@@ -104,6 +143,10 @@ pub async fn handle_get(
ObjectVersionData::FirstBlock(meta, _) => meta,
};
+ if let Some(cached) = try_answer_cached(&last_v, last_v_meta, req) {
+ return Ok(cached);
+ }
+
let range = match req.headers().get("range") {
Some(range) => {
let range_str = range.to_str()?;
diff --git a/src/web/web_server.rs b/src/web/web_server.rs
index 24d111a9..cfde2bcc 100644
--- a/src/web/web_server.rs
+++ b/src/web/web_server.rs
@@ -99,7 +99,7 @@ async fn serve_file(garage: Arc<Garage>, req: Request<Body>) -> Result<Response<
info!("Selected bucket: \"{}\", selected key: \"{}\"", bucket, key);
let res = match req.method() {
- &Method::HEAD => handle_head(garage, &bucket, &key).await?,
+ &Method::HEAD => handle_head(garage, &req, &bucket, &key).await?,
&Method::GET => handle_get(garage, &req, bucket, &key).await?,
_ => return Err(Error::BadRequest(format!("HTTP method not supported"))),
};