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 23216a129 fix(services/s3): encode version id for stat and read (#7886)
23216a129 is described below

commit 23216a129123204ff71d70b50daa214f14941854
Author: QuakeWang <[email protected]>
AuthorDate: Wed Jul 8 17:44:53 2026 +0800

    fix(services/s3): encode version id for stat and read (#7886)
    
    Signed-off-by: QuakeWang <[email protected]>
---
 core/services/s3/src/backend.rs | 55 +++++++++++++++++++++++++++++++++++++++++
 core/services/s3/src/core.rs    |  4 +--
 2 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/core/services/s3/src/backend.rs b/core/services/s3/src/backend.rs
index f6002d5a2..bceac617d 100644
--- a/core/services/s3/src/backend.rs
+++ b/core/services/s3/src/backend.rs
@@ -1359,4 +1359,59 @@ mod tests {
             "application/json"
         );
     }
+
+    #[tokio::test]
+    async fn test_presign_stat_encodes_version_id() {
+        let backend = S3Builder::default()
+            .bucket("test")
+            .region("us-east-1")
+            .skip_signature()
+            .disable_config_load()
+            .disable_ec2_metadata()
+            .build()
+            .expect("build");
+
+        let op = OpStat::default().with_version("a+b/c=d%25&e");
+        let args = OpPresign::new(op, Duration::from_secs(3600));
+        let ctx = OperationContext::new();
+        let presigned = backend
+            .presign(&ctx, "test.txt", args)
+            .await
+            .expect("presign")
+            .into_presigned_request();
+
+        assert_eq!(
+            presigned.uri().to_string(),
+            
"https://s3.us-east-1.amazonaws.com/test/test.txt?versionId=a%2Bb/c%3Dd%2525%26e";
+        );
+    }
+
+    #[tokio::test]
+    async fn test_presign_read_encodes_version_id() {
+        let backend = S3Builder::default()
+            .bucket("test")
+            .region("us-east-1")
+            .skip_signature()
+            .disable_config_load()
+            .disable_ec2_metadata()
+            .build()
+            .expect("build");
+
+        let op = OpRead::default().with_version("a+b/c=d%25&e");
+        let args = OpPresign::new(
+            PresignOperation::Read(BytesRange::default(), op),
+            Duration::from_secs(3600),
+        );
+        let ctx = OperationContext::new();
+        let presigned = backend
+            .presign(&ctx, "test.txt", args)
+            .await
+            .expect("presign")
+            .into_presigned_request();
+
+        assert_eq!(
+            presigned.uri().to_string(),
+            
"https://s3.us-east-1.amazonaws.com/test/test.txt?versionId=a%2Bb/c%3Dd%2525%26e";
+        );
+    }
 }
diff --git a/core/services/s3/src/core.rs b/core/services/s3/src/core.rs
index a66d204ee..12f9cc7fb 100644
--- a/core/services/s3/src/core.rs
+++ b/core/services/s3/src/core.rs
@@ -420,7 +420,7 @@ impl S3Core {
             query_args.push(format!(
                 "{}={}",
                 constants::S3_QUERY_VERSION_ID,
-                percent_decode_path(version)
+                percent_encode_path(version)
             ))
         }
         if !query_args.is_empty() {
@@ -496,7 +496,7 @@ impl S3Core {
             query_args.push(format!(
                 "{}={}",
                 constants::S3_QUERY_VERSION_ID,
-                percent_decode_path(version)
+                percent_encode_path(version)
             ))
         }
         if !query_args.is_empty() {

Reply via email to