This is an automated email from the ASF dual-hosted git repository.

mneumann pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs-object-store.git


The following commit(s) were added to refs/heads/main by this push:
     new 719a29d  fix: support `AWS_REQUEST_PAYER=requester` (#669)
719a29d is described below

commit 719a29d0f6eca84268c61b0969d8dbe952e75e92
Author: Len Strnad <[email protected]>
AuthorDate: Tue Mar 31 00:54:36 2026 -0600

    fix: support `AWS_REQUEST_PAYER=requester` (#669)
---
 src/aws/builder.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 95 insertions(+), 7 deletions(-)

diff --git a/src/aws/builder.rs b/src/aws/builder.rs
index 1b9204c..85dbaae 100644
--- a/src/aws/builder.rs
+++ b/src/aws/builder.rs
@@ -188,7 +188,7 @@ pub struct AmazonS3Builder {
     /// base64-encoded 256-bit customer encryption key for SSE-C.
     encryption_customer_key_base64: Option<String>,
     /// When set to true, charge requester for bucket operations
-    request_payer: ConfigValue<bool>,
+    request_payer: ConfigValue<RequesterPayer>,
     /// The [`HttpConnector`] to use
     http_connector: Option<Arc<dyn HttpConnector>>,
 }
@@ -574,7 +574,7 @@ impl AmazonS3Builder {
     /// * `AWS_CONTAINER_CREDENTIALS_FULL_URI` -> 
<https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html>
     /// * `AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE` -> 
<https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html>
     /// * `AWS_ALLOW_HTTP` -> set to "true" to permit HTTP connections without 
TLS
-    /// * `AWS_REQUEST_PAYER` -> set to "true" to permit operations on 
requester-pays buckets.
+    /// * `AWS_REQUEST_PAYER` -> set to "requester" or "true" to permit 
operations on requester-pays buckets.
     ///
     /// # Example
     /// ```
@@ -678,9 +678,7 @@ impl AmazonS3Builder {
             AmazonS3ConfigKey::ConditionalPut => {
                 self.conditional_put = ConfigValue::Deferred(value.into())
             }
-            AmazonS3ConfigKey::RequestPayer => {
-                self.request_payer = ConfigValue::Deferred(value.into())
-            }
+            AmazonS3ConfigKey::RequestPayer => self.request_payer.parse(value),
             AmazonS3ConfigKey::Encryption(key) => match key {
                 S3EncryptionConfigKey::ServerSideEncryption => {
                     self.encryption_type = 
Some(ConfigValue::Deferred(value.into()))
@@ -1061,7 +1059,7 @@ impl AmazonS3Builder {
     ///
     /// 
<https://docs.aws.amazon.com/AmazonS3/latest/userguide/RequesterPaysBuckets.html>
     pub fn with_request_payer(mut self, enabled: bool) -> Self {
-        self.request_payer = ConfigValue::Parsed(enabled);
+        self.request_payer = ConfigValue::Parsed(enabled.into());
         self
     }
 
@@ -1247,7 +1245,7 @@ impl AmazonS3Builder {
             copy_if_not_exists,
             conditional_put: self.conditional_put.get()?,
             encryption_headers,
-            request_payer: self.request_payer.get()?,
+            request_payer: self.request_payer.get()?.into(),
         };
 
         let http_client = http.connect(&config.client_options)?;
@@ -1267,6 +1265,40 @@ fn parse_bucket_az(bucket: &str) -> Option<&str> {
     Some(base.rsplit_once("--")?.1)
 }
 
+/// Captures `AWS_REQUEST_PAYER`.
+///
+/// Parses either as `"requester"` (case-insensitive) meaning `true`, or as a 
[`bool`] (i.e. `"true"`, `"1"`, `"no"`, etc.).
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
+struct RequesterPayer(bool);
+
+impl crate::config::Parse for RequesterPayer {
+    fn parse(v: &str) -> Result<Self> {
+        if v.eq_ignore_ascii_case("requester") {
+            Ok(Self(true))
+        } else {
+            Ok(Self(<bool as crate::config::Parse>::parse(v)?))
+        }
+    }
+}
+
+impl From<bool> for RequesterPayer {
+    fn from(value: bool) -> Self {
+        Self(value)
+    }
+}
+
+impl From<RequesterPayer> for bool {
+    fn from(value: RequesterPayer) -> Self {
+        value.0
+    }
+}
+
+impl std::fmt::Display for RequesterPayer {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
 /// Encryption configuration options for S3.
 ///
 /// These options are used to configure server-side encryption for S3 objects.
@@ -1783,6 +1815,62 @@ mod tests {
             err,
             "Generic Config error: \"md5\" is not a valid checksum algorithm"
         );
+
+        let err = AmazonS3Builder::new()
+            .with_config(AmazonS3ConfigKey::RequestPayer, "requestr")
+            .with_bucket_name("bucket")
+            .with_region("region")
+            .build()
+            .unwrap_err()
+            .to_string();
+
+        assert_eq!(
+            err,
+            "Generic Config error: failed to parse \"requestr\" as boolean"
+        );
+    }
+
+    #[test]
+    fn test_request_payer_config() {
+        let s3 = AmazonS3Builder::new()
+            .with_config(AmazonS3ConfigKey::RequestPayer, "requester")
+            .with_bucket_name("bucket")
+            .with_region("region")
+            .build()
+            .unwrap();
+        assert!(s3.client.config.request_payer);
+
+        let s3 = AmazonS3Builder::new()
+            .with_config(AmazonS3ConfigKey::RequestPayer, "REQUESTER")
+            .with_bucket_name("bucket")
+            .with_region("region")
+            .build()
+            .unwrap();
+        assert!(s3.client.config.request_payer);
+
+        let s3 = AmazonS3Builder::new()
+            .with_config(AmazonS3ConfigKey::RequestPayer, "true")
+            .with_bucket_name("bucket")
+            .with_region("region")
+            .build()
+            .unwrap();
+        assert!(s3.client.config.request_payer);
+
+        let s3 = AmazonS3Builder::new()
+            .with_config(AmazonS3ConfigKey::RequestPayer, "false")
+            .with_bucket_name("bucket")
+            .with_region("region")
+            .build()
+            .unwrap();
+        assert!(!s3.client.config.request_payer);
+
+        let s3 = AmazonS3Builder::new()
+            .with_request_payer(true)
+            .with_bucket_name("bucket")
+            .with_region("region")
+            .build()
+            .unwrap();
+        assert!(s3.client.config.request_payer);
     }
 
     #[test]

Reply via email to