Skip to content

fix: support AWS_REQUEST_PAYER=requester#669

Open
ljstrnadiii wants to merge 4 commits intoapache:mainfrom
ljstrnadiii:support_aws_request_payer_requester
Open

fix: support AWS_REQUEST_PAYER=requester#669
ljstrnadiii wants to merge 4 commits intoapache:mainfrom
ljstrnadiii:support_aws_request_payer_requester

Conversation

@ljstrnadiii
Copy link

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
}
AmazonS3ConfigKey::RequestPayer => {
self.request_payer = ConfigValue::Deferred(value.into())
// Support the standard AWS value "requester" as a boolean true
Copy link
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
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
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
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
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
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
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.

Copy link
Contributor

@crepererum crepererum left a comment

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.


#[test]
fn test_request_payer_config() {
let s3 = AmazonS3Builder::new()
Copy link
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?

Some(bucket.strip_suffix("--x-s3")?.rsplit_once("--")?.1)
}

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