Skip to content

fix: support AWS_REQUEST_PAYER=requester#669

Merged
crepererum merged 4 commits into
apache:mainfrom
ljstrnadiii:support_aws_request_payer_requester
Mar 31, 2026
Merged

fix: support AWS_REQUEST_PAYER=requester#669
crepererum merged 4 commits into
apache:mainfrom
ljstrnadiii:support_aws_request_payer_requester

Conversation

@ljstrnadiii

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #668

Rationale for this change

Avoid AWS_REQUEST_PAYER env var collision.

What changes are included in this PR?

Resolves 'requester' to True for requester_pays

Are there any user-facing changes?

No.

@ljstrnadiii ljstrnadiii changed the title fix: support "requester" for AWS_REQUEST_PAYER env var fix: support AWS_REQUEST_PAYER=requester Mar 18, 2026
Comment thread src/aws/builder.rs Outdated
}
AmazonS3ConfigKey::RequestPayer => {
self.request_payer = ConfigValue::Deferred(value.into())
// Support the standard AWS value "requester" as a boolean true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the parsing logic should be part of the value type, like this:

#[derive(Debug, Clone)]
enum S3EncryptionType {
S3,
SseKms,
DsseKms,
SseC,
}
impl crate::config::Parse for S3EncryptionType {
fn parse(s: &str) -> Result<Self> {
match s {
"AES256" => Ok(Self::S3),
"aws:kms" => Ok(Self::SseKms),
"aws:kms:dsse" => Ok(Self::DsseKms),
"sse-c" => Ok(Self::SseC),
_ => Err(Error::InvalidEncryptionType { passed: s.into() }.into()),
}
}
}
impl From<&S3EncryptionType> for &'static str {
fn from(value: &S3EncryptionType) -> Self {
match value {
S3EncryptionType::S3 => "AES256",
S3EncryptionType::SseKms => "aws:kms",
S3EncryptionType::DsseKms => "aws:kms:dsse",
S3EncryptionType::SseC => "sse-c",
}
}
}
impl std::fmt::Display for S3EncryptionType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.into())
}
}

Now from the user PoV this doesn't make a difference, but using this pattern makes the code easier to read and maintain (no surprising "why is this value handled differently and where is the code that does this out of the sudden?" surprises)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the guidance. Will make this change today.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crepererum Putting this as part of the impl Parse for bool

impl Parse for bool {
fn parse(v: &str) -> Result<Self> {
let lower = v.to_ascii_lowercase();
match lower.as_str() {
"1" | "true" | "on" | "yes" | "y" => Ok(true),
"0" | "false" | "off" | "no" | "n" => Ok(false),
_ => Err(Error::Generic {
store: "Config",
source: format!("failed to parse \"{v}\" as boolean").into(),
}),
}
}
}

means that any boolean flag is enabled by the string "requester", right?

From my point of view if we allow this case, it seems perhaps better to isolate it to the AWS_REQUEST_PAYER code path? 🤷‍♂️

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also went back and forth on this, but @kylebarron has a good point. I'll wait for followup from @crepererum before I do anything here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably use a new-type wrapper for that, like:

// derive whatever you need, but basically this is just a `bool`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash]
struct RequesterPayer(bool);

impl Parse for RequesterPayer {
    fn parse(v: &str) -> Result<Self> {
        if v.eq_ignore_ascii_case("requester") {
            Ok(Self(true))
        } else {
            Ok(Self(bool::parse(v)?))
        }
    }
}

// you may want to implement a few convenience traits
impl From<RequesterPayer> for bool {...}

impl AsRef<bool> for RequesterPayer {...}

impl Deref for RequesterPayer {...}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newtype wrapper sounds great.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roger that. Thanks all.

@crepererum crepererum left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nitpicks, but overall this looks pretty good already.

Comment thread src/aws/builder.rs

#[test]
fn test_request_payer_config() {
let s3 = AmazonS3Builder::new()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I get a case-insensitive test case too, e.g. Requester or REQUESTER?

Comment thread src/aws/builder.rs
Some(bucket.strip_suffix("--x-s3")?.rsplit_once("--")?.1)
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
/// 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)]

Feel free to rephrase, but I think leaving some documentation might be nice.

@ljstrnadiii
ljstrnadiii requested a review from crepererum March 20, 2026 14:23
@ljstrnadiii

Copy link
Copy Markdown
Contributor Author

@crepererum can we merge?

@crepererum
crepererum merged commit 719a29d into apache:main Mar 31, 2026
9 checks passed
@crepererum

Copy link
Copy Markdown
Contributor

Are you OK if we put this into the next major release (0.14.x) or do you need this as part of the 0.13.x series?

@kylebarron

Copy link
Copy Markdown
Member

I think @ljstrnadiii might just need this exposed through Obstore, which we can do by publishing a Python version on this Git tag. Or do you need this as a rust release @ljstrnadiii?

@ljstrnadiii

Copy link
Copy Markdown
Contributor Author

Correct, we need this through obstore.

@kylebarron

Copy link
Copy Markdown
Member

Good to know. Feel free to make an issue in obstore to update to this Git hash. Ideally we'd go commit-by-commit so that we ensure we expose all underlying features through to obstore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AWS_REQUEST_PAYER="requester" vs Boolean

4 participants