Merge pull request 'add support for caching headers' (#49) from trinity-1686a/garage:cache-headers into master
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #49
This commit is contained in:
commit
bfa0ff8f82
3 changed files with 46 additions and 3 deletions
|
@ -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
|
||||||
|
|
|
@ -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()?;
|
||||||
|
|
|
@ -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"))),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue