add support for caching headers

This commit is contained in:
Trinity Pointard 2021-03-18 15:46:33 +01:00
parent 797cda1c33
commit b4c903371c
3 changed files with 46 additions and 3 deletions

View file

@ -101,7 +101,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
match req.method() { match req.method() {
&Method::HEAD => { &Method::HEAD => {
// HeadObject query // HeadObject query
Ok(handle_head(garage, &bucket, &key).await?) Ok(handle_head(garage, &req, &bucket, &key).await?)
} }
&Method::GET => { &Method::GET => {
// GetObject query // GetObject query

View file

@ -40,8 +40,43 @@ fn object_headers(
resp 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( pub async fn handle_head(
garage: Arc<Garage>, garage: Arc<Garage>,
req: &Request<Body>,
bucket: &str, bucket: &str,
key: &str, key: &str,
) -> Result<Response<Body>, Error> { ) -> Result<Response<Body>, Error> {
@ -65,7 +100,11 @@ pub async fn handle_head(
_ => unreachable!(), _ => 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) let response = object_headers(&version, version_meta)
.header("Content-Length", format!("{}", version_meta.size)) .header("Content-Length", format!("{}", version_meta.size))
.status(StatusCode::OK) .status(StatusCode::OK)
@ -104,6 +143,10 @@ pub async fn handle_get(
ObjectVersionData::FirstBlock(meta, _) => meta, 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") { let range = match req.headers().get("range") {
Some(range) => { Some(range) => {
let range_str = range.to_str()?; let range_str = range.to_str()?;

View file

@ -99,7 +99,7 @@ async fn serve_file(garage: Arc<Garage>, req: Request<Body>) -> Result<Response<
info!("Selected bucket: \"{}\", selected key: \"{}\"", bucket, key); info!("Selected bucket: \"{}\", selected key: \"{}\"", bucket, key);
let res = match req.method() { 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?, &Method::GET => handle_get(garage, &req, bucket, &key).await?,
_ => return Err(Error::BadRequest(format!("HTTP method not supported"))), _ => return Err(Error::BadRequest(format!("HTTP method not supported"))),
}; };