diff --git a/core/src/services/s3/backend.rs b/core/src/services/s3/backend.rs index c3fa6aac1cc..da2f1181715 100644 --- a/core/src/services/s3/backend.rs +++ b/core/src/services/s3/backend.rs @@ -944,7 +944,7 @@ impl Accessor for S3Backend { async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> { let resp = self .core - .s3_get_object(path, args.range(), args.if_none_match()) + .s3_get_object(path, args.range(), args.if_none_match(), args.if_match()) .await?; let status = resp.status(); @@ -967,6 +967,7 @@ impl Accessor for S3Backend { args.content_type(), args.content_disposition(), args.cache_control(), + args.if_match(), ) .await?; @@ -1017,7 +1018,10 @@ impl Accessor for S3Backend { return Ok(RpStat::new(Metadata::new(EntryMode::DIR))); } - let resp = self.core.s3_head_object(path, args.if_none_match()).await?; + let resp = self + .core + .s3_head_object(path, args.if_none_match(), args.if_match()) + .await?; let status = resp.status(); @@ -1059,7 +1063,8 @@ impl Accessor for S3Backend { // We will not send this request out, just for signing. let mut req = match args.operation() { PresignOperation::Stat(v) => { - self.core.s3_head_object_request(path, v.if_none_match())? + self.core + .s3_head_object_request(path, v.if_none_match(), v.if_match())? } PresignOperation::Read(v) => self.core.s3_get_object_request( path, @@ -1067,6 +1072,7 @@ impl Accessor for S3Backend { v.override_content_disposition(), v.override_cache_control(), v.if_none_match(), + v.if_match(), )?, PresignOperation::Write(_) => { self.core diff --git a/core/src/services/s3/core.rs b/core/src/services/s3/core.rs index b9f2281a75f..c618b87d6e5 100644 --- a/core/src/services/s3/core.rs +++ b/core/src/services/s3/core.rs @@ -29,6 +29,7 @@ use http::header::CACHE_CONTROL; use http::header::CONTENT_DISPOSITION; use http::header::CONTENT_LENGTH; use http::header::CONTENT_TYPE; +use http::header::IF_MATCH; use http::header::IF_NONE_MATCH; use http::HeaderValue; use http::Request; @@ -206,6 +207,7 @@ impl S3Core { &self, path: &str, if_none_match: Option<&str>, + if_match: Option<&str>, ) -> Result> { let p = build_abs_path(&self.root, path); @@ -219,6 +221,10 @@ impl S3Core { req = req.header(IF_NONE_MATCH, if_none_match); } + if let Some(if_match) = if_match { + req = req.header(IF_MATCH, if_match); + } + let req = req .body(AsyncBody::Empty) .map_err(new_request_build_error)?; @@ -233,6 +239,7 @@ impl S3Core { override_content_disposition: Option<&str>, override_cache_control: Option<&str>, if_none_match: Option<&str>, + if_match: Option<&str>, ) -> Result> { let p = build_abs_path(&self.root, path); @@ -269,6 +276,9 @@ impl S3Core { req = req.header(IF_NONE_MATCH, if_none_match); } + if let Some(if_match) = if_match { + req = req.header(IF_MATCH, if_match); + } // Set SSE headers. // TODO: how will this work with presign? req = self.insert_sse_headers(req, false); @@ -285,8 +295,10 @@ impl S3Core { path: &str, range: BytesRange, if_none_match: Option<&str>, + if_match: Option<&str>, ) -> Result> { - let mut req = self.s3_get_object_request(path, range, None, None, if_none_match)?; + let mut req = + self.s3_get_object_request(path, range, None, None, if_none_match, if_match)?; self.sign(&mut req).await?; @@ -342,8 +354,9 @@ impl S3Core { &self, path: &str, if_none_match: Option<&str>, + if_match: Option<&str>, ) -> Result> { - let mut req = self.s3_head_object_request(path, if_none_match)?; + let mut req = self.s3_head_object_request(path, if_none_match, if_match)?; self.sign(&mut req).await?; @@ -476,6 +489,7 @@ impl S3Core { content_type: Option<&str>, content_disposition: Option<&str>, cache_control: Option<&str>, + if_match: Option<&str>, ) -> Result> { let p = build_abs_path(&self.root, path); @@ -495,6 +509,10 @@ impl S3Core { req = req.header(CACHE_CONTROL, cache_control) } + if let Some(if_match) = if_match { + req = req.header(IF_MATCH, if_match) + } + // Set storage class header if let Some(v) = &self.default_storage_class { req = req.header(HeaderName::from_static(constants::X_AMZ_STORAGE_CLASS), v);