This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 0f8b4613d feat(services/swift): add conditional request headers for
stat and read (#7208)
0f8b4613d is described below
commit 0f8b4613dae906484ece0b270f02d729472bb344
Author: Ben Roeder <[email protected]>
AuthorDate: Sun Feb 22 16:49:26 2026 -0800
feat(services/swift): add conditional request headers for stat and read
(#7208)
feat(services/swift): add conditional request headers
Add If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since
support for stat and read operations.
Map HTTP 304 Not Modified to ConditionNotMatch error kind.
Note: Swift does not support If-Match on PUT and only supports
If-None-Match: * (not specific etag values) on PUT, so write
conditional capabilities are not declared.
---
core/services/swift/src/backend.rs | 13 +++++++++++--
core/services/swift/src/core.rs | 34 ++++++++++++++++++++++++++++++++--
core/services/swift/src/error.rs | 4 +++-
3 files changed, 46 insertions(+), 5 deletions(-)
diff --git a/core/services/swift/src/backend.rs
b/core/services/swift/src/backend.rs
index 088e54abd..3bc7bd00a 100644
--- a/core/services/swift/src/backend.rs
+++ b/core/services/swift/src/backend.rs
@@ -144,7 +144,16 @@ impl Builder for SwiftBuilder {
.set_root(&root)
.set_native_capability(Capability {
stat: true,
+ stat_with_if_match: true,
+ stat_with_if_none_match: true,
+ stat_with_if_modified_since: true,
+ stat_with_if_unmodified_since: true,
+
read: true,
+ read_with_if_match: true,
+ read_with_if_none_match: true,
+ read_with_if_modified_since: true,
+ read_with_if_unmodified_since: true,
write: true,
write_can_empty: true,
@@ -192,8 +201,8 @@ impl Access for SwiftBackend {
self.core.info.clone()
}
- async fn stat(&self, path: &str, _args: OpStat) -> Result<RpStat> {
- let resp = self.core.swift_get_metadata(path).await?;
+ async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
+ let resp = self.core.swift_get_metadata(path, &args).await?;
match resp.status() {
StatusCode::OK | StatusCode::NO_CONTENT => {
diff --git a/core/services/swift/src/core.rs b/core/services/swift/src/core.rs
index ef837f531..6301b7974 100644
--- a/core/services/swift/src/core.rs
+++ b/core/services/swift/src/core.rs
@@ -21,6 +21,10 @@ use std::sync::Arc;
use http::Request;
use http::Response;
use http::header;
+use http::header::IF_MATCH;
+use http::header::IF_MODIFIED_SINCE;
+use http::header::IF_NONE_MATCH;
+use http::header::IF_UNMODIFIED_SINCE;
use serde::Deserialize;
use opendal_core::raw::*;
@@ -156,7 +160,7 @@ impl SwiftCore {
&self,
path: &str,
range: BytesRange,
- _arg: &OpRead,
+ args: &OpRead,
) -> Result<Response<HttpBody>> {
let p = build_abs_path(&self.root, path)
.trim_end_matches('/')
@@ -177,6 +181,19 @@ impl SwiftCore {
req = req.header(header::RANGE, range.to_header());
}
+ if let Some(if_match) = args.if_match() {
+ req = req.header(IF_MATCH, if_match);
+ }
+ if let Some(if_none_match) = args.if_none_match() {
+ req = req.header(IF_NONE_MATCH, if_none_match);
+ }
+ if let Some(if_modified_since) = args.if_modified_since() {
+ req = req.header(IF_MODIFIED_SINCE,
if_modified_since.format_http_date());
+ }
+ if let Some(if_unmodified_since) = args.if_unmodified_since() {
+ req = req.header(IF_UNMODIFIED_SINCE,
if_unmodified_since.format_http_date());
+ }
+
let req = req
.extension(Operation::Read)
.body(Buffer::new())
@@ -225,7 +242,7 @@ impl SwiftCore {
self.info.http_client().send(req).await
}
- pub async fn swift_get_metadata(&self, path: &str) ->
Result<Response<Buffer>> {
+ pub async fn swift_get_metadata(&self, path: &str, args: &OpStat) ->
Result<Response<Buffer>> {
let p = build_abs_path(&self.root, path);
let url = format!(
@@ -239,6 +256,19 @@ impl SwiftCore {
req = req.header("X-Auth-Token", &self.token);
+ if let Some(if_match) = args.if_match() {
+ req = req.header(IF_MATCH, if_match);
+ }
+ if let Some(if_none_match) = args.if_none_match() {
+ req = req.header(IF_NONE_MATCH, if_none_match);
+ }
+ if let Some(if_modified_since) = args.if_modified_since() {
+ req = req.header(IF_MODIFIED_SINCE,
if_modified_since.format_http_date());
+ }
+ if let Some(if_unmodified_since) = args.if_unmodified_since() {
+ req = req.header(IF_UNMODIFIED_SINCE,
if_unmodified_since.format_http_date());
+ }
+
let req = req
.extension(Operation::Stat)
.body(Buffer::new())
diff --git a/core/services/swift/src/error.rs b/core/services/swift/src/error.rs
index a062ac6d9..32d5d57c1 100644
--- a/core/services/swift/src/error.rs
+++ b/core/services/swift/src/error.rs
@@ -39,7 +39,9 @@ pub(super) fn parse_error(resp: Response<Buffer>) -> Error {
let (kind, retryable) = match parts.status {
StatusCode::NOT_FOUND => (ErrorKind::NotFound, false),
StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN =>
(ErrorKind::PermissionDenied, false),
- StatusCode::PRECONDITION_FAILED => (ErrorKind::ConditionNotMatch,
false),
+ StatusCode::NOT_MODIFIED | StatusCode::PRECONDITION_FAILED => {
+ (ErrorKind::ConditionNotMatch, false)
+ }
StatusCode::INTERNAL_SERVER_ERROR
| StatusCode::BAD_GATEWAY
| StatusCode::SERVICE_UNAVAILABLE