diff options
author | Trinity Pointard <trinity.pointard@gmail.com> | 2021-03-18 15:46:33 +0100 |
---|---|---|
committer | Trinity Pointard <trinity.pointard@gmail.com> | 2021-03-18 15:46:33 +0100 |
commit | b4c903371c3812851befd1af8f01419fe18efcf3 (patch) | |
tree | 896ef4e1729e74bf9ed4068c7121a37d0353ba4d | |
parent | 797cda1c33d0b908492d2c08ece1c51c9b10f36d (diff) | |
download | garage-b4c903371c3812851befd1af8f01419fe18efcf3.tar.gz garage-b4c903371c3812851befd1af8f01419fe18efcf3.zip |
add support for caching headers
-rw-r--r-- | src/api/api_server.rs | 2 | ||||
-rw-r--r-- | src/api/s3_get.rs | 45 | ||||
-rw-r--r-- | src/web/web_server.rs | 2 |
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"))), }; |