Skip to content

Add expectContinueThresholdInBytes config to S3#6864

Open
alextwoods wants to merge 9 commits intomasterfrom
alexwoo/s3_100cont_threshold
Open

Add expectContinueThresholdInBytes config to S3#6864
alextwoods wants to merge 9 commits intomasterfrom
alexwoo/s3_100cont_threshold

Conversation

@alextwoods
Copy link
Copy Markdown
Contributor

@alextwoods alextwoods commented Apr 15, 2026

Motivation and Context

The Expect: 100-continue header allows a client to check whether the server will accept a request before sending the body. While this saves bandwidth on rejected requests, it adds a full round-trip of latency on every successful request. Benchmark data shows this overhead can increase latency by up to 30% for small uploads (under 1 MB) on public internet.

Currently the SDK unconditionally adds the header to all PutObject and UploadPart requests (when expectContinueEnabled is true). This change introduces a configurable content-length threshold so the header is skipped for small payloads where the cost outweighs the benefit.

This is consistent with other AWS SDKs — JavaScript V3 and Kotlin both use a content-length threshold for this header.

Related to #6774 and #6828

Modifications

  • Added expectContinueThresholdInBytes configuration with default of 1MB.

Testing

new + existing unit tests.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING document
  • Local run of mvn install succeeds
  • My code follows the code style of this project
  • My change requires a change to the Javadoc documentation
  • I have updated the Javadoc documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed
  • I have added a changelog entry. Adding a new entry must be accomplished by running the scripts/new-change script and following the instructions. Commit the new file created by the script in .changes/next-release with your changes.
  • My change is to implement 1.11 parity feature and I have updated LaunchChangelog

License

  • I confirm that this pull request can be released under the Apache 2 license

@alextwoods alextwoods requested a review from a team as a code owner April 15, 2026 16:32
@alextwoods alextwoods added the api-surface-area-approved-by-team Indicate API surface area introduced by this PR has been approved by team label Apr 16, 2026
Copy link
Copy Markdown
Contributor

@Fred1155 Fred1155 left a comment

Choose a reason for hiding this comment

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

Should we modify the Expect100ContinueHeaderTest as well?


private static final String DECODED_CONTENT_LENGTH_HEADER = "x-amz-decoded-content-length";
private static final String CONTENT_LENGTH_HEADER = "Content-Length";
private static final long DEFAULT_EXPECT_CONTINUE_THRESHOLD_IN_BYTES = 1_048_576L;
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.

Trying to find a better way to define DEFAULT_EXPECT_CONTINUE_THRESHOLD_IN_BYTES in only one place in case of when changing values, we only changed one of them. But seems difficult.

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.

Do you think it is better to create a new S3configuration using builder.build() and get the default value out of it when needed at line 66 instead of defining another one here?

Copy link
Copy Markdown
Contributor Author

@alextwoods alextwoods Apr 16, 2026

Choose a reason for hiding this comment

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

Yeah good call - I think the right place for the default is in S3Configuration as thats how we do other defaults.

The previous logic was handling the case where S3config isn't available - I'm not sure when that would ever actually happen. I've updated this logic to just default to using 0 when there isn't a config, effectively disabling the threshold.

// -----------------------------------------------------------------------

@Test
void modifyHttpRequest_putObject_contentLengthAboveThreshold_shouldAddExpectHeader() {
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.

nit: can we use parameterized tests?

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.

Yeah, good idea! Refactored these to be parameterized.

Comment on lines +109 to +113
if (this.expectContinueThresholdInBytes.value() < 0) {
throw new IllegalArgumentException(
"expectContinueThresholdInBytes must not be negative, but was: "
+ this.expectContinueThresholdInBytes.value());
}
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.

Forgot to raise this earlier... should we throw error if expectContinueEnabled is false and expectContinueThresholdInBytes is configured? Don't remember how we handle it for mutlipartEnabled vs multipartConfiguration, let me check

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.

Yeah - good call out - I believe for multipartEnable + multipartConfiguration we do not throw - we just silently ignore the multipartConfiguration.

@alextwoods
Copy link
Copy Markdown
Contributor Author

Should we modify the Expect100ContinueHeaderTest as well?

Yeah, good question. I didn't add anything to these tests since the documentation on them says "Functional tests to verify RFC 9110 compliance". And that behavior hasn't changed. Those tests don't test the pre-existing S3 expectContinueEnabled configuration either, so I didn't update them.

What do you think?

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
3.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

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

Labels

api-surface-area-approved-by-team Indicate API surface area introduced by this PR has been approved by team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants