Skip to content

Commit

Permalink
feat: add if_none_match support (#1832)
Browse files Browse the repository at this point in the history
* feat: add if_none_match support

* use Self
  • Loading branch information
gitccl authored Apr 1, 2023
1 parent e8fd466 commit 5787e99
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 9 deletions.
39 changes: 32 additions & 7 deletions core/src/services/s3/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,9 @@ impl Accessor for S3Backend {
}

async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
let resp = self.s3_get_object(path, args.range()).await?;
let resp = self
.s3_get_object(path, args.range(), args.if_none_match())
.await?;

let status = resp.status();

Expand Down Expand Up @@ -1202,13 +1204,13 @@ impl Accessor for S3Backend {
))
}

async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
// Stat root always returns a DIR.
if path == "/" {
return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
}

let resp = self.s3_head_object(path).await?;
let resp = self.s3_head_object(path, args.if_none_match()).await?;

let status = resp.status();

Expand Down Expand Up @@ -1249,12 +1251,13 @@ impl Accessor for S3Backend {
fn presign(&self, path: &str, args: OpPresign) -> Result<RpPresign> {
// We will not send this request out, just for signing.
let mut req = match args.operation() {
PresignOperation::Stat(_) => self.s3_head_object_request(path)?,
PresignOperation::Stat(v) => self.s3_head_object_request(path, v.if_none_match())?,
PresignOperation::Read(v) => self.s3_get_object_request(
path,
v.range(),
v.override_content_disposition(),
v.override_cache_control(),
v.if_none_match(),
)?,
PresignOperation::Write(_) => {
self.s3_put_object_request(path, None, None, None, None, AsyncBody::Empty)?
Expand Down Expand Up @@ -1325,7 +1328,11 @@ impl Accessor for S3Backend {
}

impl S3Backend {
fn s3_head_object_request(&self, path: &str) -> Result<Request<AsyncBody>> {
fn s3_head_object_request(
&self,
path: &str,
if_none_match: Option<&str>,
) -> Result<Request<AsyncBody>> {
let p = build_abs_path(&self.root, path);

let url = format!("{}/{}", self.endpoint, percent_encode_path(&p));
Expand All @@ -1334,6 +1341,10 @@ impl S3Backend {

req = self.insert_sse_headers(req, false);

if let Some(if_none_match) = if_none_match {
req = req.header(http::header::IF_NONE_MATCH, if_none_match);
}

let req = req
.body(AsyncBody::Empty)
.map_err(new_request_build_error)?;
Expand All @@ -1347,6 +1358,7 @@ impl S3Backend {
range: BytesRange,
override_content_disposition: Option<&str>,
override_cache_control: Option<&str>,
if_none_match: Option<&str>,
) -> Result<Request<AsyncBody>> {
let p = build_abs_path(&self.root, path);

Expand Down Expand Up @@ -1379,6 +1391,10 @@ impl S3Backend {
req = req.header(http::header::RANGE, range.to_header());
}

if let Some(if_none_match) = if_none_match {
req = req.header(http::header::IF_NONE_MATCH, if_none_match);
}

// Set SSE headers.
// TODO: how will this work with presign?
req = self.insert_sse_headers(req, false);
Expand All @@ -1394,8 +1410,9 @@ impl S3Backend {
&self,
path: &str,
range: BytesRange,
if_none_match: Option<&str>,
) -> Result<Response<IncomingAsyncBody>> {
let mut req = self.s3_get_object_request(path, range, None, None)?;
let mut req = self.s3_get_object_request(path, range, None, None, if_none_match)?;

self.signer.sign(&mut req).map_err(new_request_sign_error)?;

Expand Down Expand Up @@ -1442,7 +1459,11 @@ impl S3Backend {
Ok(req)
}

async fn s3_head_object(&self, path: &str) -> Result<Response<IncomingAsyncBody>> {
async fn s3_head_object(
&self,
path: &str,
if_none_match: Option<&str>,
) -> Result<Response<IncomingAsyncBody>> {
let p = build_abs_path(&self.root, path);

let url = format!("{}/{}", self.endpoint, percent_encode_path(&p));
Expand All @@ -1452,6 +1473,10 @@ impl S3Backend {
// Set SSE headers.
req = self.insert_sse_headers(req, false);

if let Some(if_none_match) = if_none_match {
req = req.header(http::header::IF_NONE_MATCH, if_none_match);
}

let mut req = req
.body(AsyncBody::Empty)
.map_err(new_request_build_error)?;
Expand Down
29 changes: 27 additions & 2 deletions core/src/types/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ pub struct OpRead {
br: BytesRange,
override_content_disposition: Option<String>,
override_cache_control: Option<String>,
if_none_match: Option<String>,
}

impl OpRead {
Expand Down Expand Up @@ -273,16 +274,40 @@ impl OpRead {
pub fn override_cache_control(&self) -> Option<&str> {
self.override_cache_control.as_deref()
}

/// Set the If-None-Match of the option
pub fn with_if_none_match(mut self, if_none_match: &str) -> Self {
self.if_none_match = Some(if_none_match.to_string());
self
}

/// Get If-None-Match from option
pub fn if_none_match(&self) -> Option<&str> {
self.if_none_match.as_deref()
}
}

/// Args for `stat` operation.
#[derive(Debug, Clone, Default)]
pub struct OpStat {}
pub struct OpStat {
if_none_match: Option<String>,
}

impl OpStat {
/// Create a new `OpStat`.
pub fn new() -> Self {
Self {}
Self::default()
}

/// Set the If-None-Match of the option
pub fn with_if_none_match(mut self, if_none_match: &str) -> Self {
self.if_none_match = Some(if_none_match.to_string());
self
}

/// Get If-None-Match from option
pub fn if_none_match(&self) -> Option<&str> {
self.if_none_match.as_deref()
}
}

Expand Down

0 comments on commit 5787e99

Please sign in to comment.