@@ -77,12 +77,19 @@ public final class S3Configuration implements ServiceConfiguration, ToCopyableBu
7777 */
7878 private static final boolean DEFAULT_EXPECT_CONTINUE_ENABLED = true ;
7979
80+ /**
81+ * The default minimum content-length in bytes at which the {@code Expect: 100-continue} header is added.
82+ * Requests with a content-length below this threshold will not include the header.
83+ */
84+ private static final long DEFAULT_EXPECT_CONTINUE_THRESHOLD_IN_BYTES = 1_048_576L ;
85+
8086 private final FieldWithDefault <Boolean > pathStyleAccessEnabled ;
8187 private final FieldWithDefault <Boolean > accelerateModeEnabled ;
8288 private final FieldWithDefault <Boolean > dualstackEnabled ;
8389 private final FieldWithDefault <Boolean > checksumValidationEnabled ;
8490 private final FieldWithDefault <Boolean > chunkedEncodingEnabled ;
8591 private final FieldWithDefault <Boolean > expectContinueEnabled ;
92+ private final FieldWithDefault <Long > expectContinueThresholdInBytes ;
8693 private final Boolean useArnRegionEnabled ;
8794 private final Boolean multiRegionEnabled ;
8895 private final FieldWithDefault <Supplier <ProfileFile >> profileFile ;
@@ -97,6 +104,13 @@ private S3Configuration(DefaultS3ServiceConfigurationBuilder builder) {
97104 this .chunkedEncodingEnabled = FieldWithDefault .create (builder .chunkedEncodingEnabled , DEFAULT_CHUNKED_ENCODING_ENABLED );
98105 this .expectContinueEnabled = FieldWithDefault .create (builder .expectContinueEnabled ,
99106 DEFAULT_EXPECT_CONTINUE_ENABLED );
107+ this .expectContinueThresholdInBytes = FieldWithDefault .create (builder .expectContinueThresholdInBytes ,
108+ DEFAULT_EXPECT_CONTINUE_THRESHOLD_IN_BYTES );
109+ if (this .expectContinueThresholdInBytes .value () < 0 ) {
110+ throw new IllegalArgumentException (
111+ "expectContinueThresholdInBytes must not be negative, but was: "
112+ + this .expectContinueThresholdInBytes .value ());
113+ }
100114 this .profileFile = FieldWithDefault .create (builder .profileFile , ProfileFile ::defaultProfileFile );
101115 this .profileName = FieldWithDefault .create (builder .profileName ,
102116 ProfileFileSystemSetting .AWS_PROFILE .getStringValueOrThrow ());
@@ -247,6 +261,26 @@ public boolean expectContinueEnabled() {
247261 return expectContinueEnabled .value ();
248262 }
249263
264+ /**
265+ * Returns the minimum content-length in bytes at which the {@code Expect: 100-continue} header is added to
266+ * {@link PutObjectRequest} and {@link UploadPartRequest}. Requests with a content-length below this threshold
267+ * will not include the header.
268+ * <p>
269+ * The default value is 1048576 bytes (1 MB).
270+ * <p>
271+ * <b>Note:</b> When using the {@code ApacheHttpClient} (Apache 4), the Apache 4 client also independently adds the
272+ * {@code Expect: 100-continue} header by default without any threshold via its own {@code expectContinueEnabled}
273+ * setting. To benefit from the `expectContinueThresholdInBytes` you must disable {@code expectContinueEnabled}
274+ * on the Apache4 HTTP client builder using {@code ApacheHttpClient.builder().expectContinueEnabled(false)}.
275+ * This does NOT apply to the {@code Apache5HttpClient} which defaults {@code expectContinueEnabled} to false.
276+ *
277+ * @return The threshold in bytes.
278+ * @see S3Configuration.Builder#expectContinueThresholdInBytes(Long)
279+ */
280+ public long expectContinueThresholdInBytes () {
281+ return expectContinueThresholdInBytes .value ();
282+ }
283+
250284 /**
251285 * Returns whether the client is allowed to make cross-region calls when an S3 Access Point ARN has a different
252286 * region to the one configured on the client.
@@ -278,6 +312,7 @@ public Builder toBuilder() {
278312 .checksumValidationEnabled (checksumValidationEnabled .valueOrNullIfDefault ())
279313 .chunkedEncodingEnabled (chunkedEncodingEnabled .valueOrNullIfDefault ())
280314 .expectContinueEnabled (expectContinueEnabled .valueOrNullIfDefault ())
315+ .expectContinueThresholdInBytes (expectContinueThresholdInBytes .valueOrNullIfDefault ())
281316 .useArnRegionEnabled (useArnRegionEnabled )
282317 .profileFile (profileFile .valueOrNullIfDefault ())
283318 .profileName (profileName .valueOrNullIfDefault ());
@@ -407,6 +442,32 @@ public interface Builder extends CopyableBuilder<Builder, S3Configuration> {
407442 */
408443 Builder expectContinueEnabled (Boolean expectContinueEnabled );
409444
445+ Long expectContinueThresholdInBytes ();
446+
447+ /**
448+ * Option to configure the minimum content-length in bytes at which the {@code Expect: 100-continue} header
449+ * is added to {@link PutObjectRequest} and {@link UploadPartRequest}. Requests with a content-length below
450+ * this threshold will not include the header, reducing latency for small uploads where the round-trip cost
451+ * of the 100-continue handshake outweighs the benefit.
452+ * <p>
453+ * The default value is 1048576 bytes (1 MB). Setting this to 0 restores the pre-threshold behavior where
454+ * the header is added for all non-zero content-length requests.
455+ * <p>
456+ * This setting only takes effect when {@link #expectContinueEnabled(Boolean)} is {@code true} (the default).
457+ * <p>
458+ * When content length is not known, the {@code Expect: 100-continue} header will always be added
459+ * when {@link #expectContinueEnabled(Boolean)} is {@code true}.
460+ * <p>
461+ * <b>Note:</b> When using the {@code ApacheHttpClient} (Apache 4), the Apache 4 client also independently adds the
462+ * {@code Expect: 100-continue} header by default via its own {@code expectContinueEnabled} setting. This threshold
463+ * only controls the SDK's own header addition; it does not affect the Apache client's behavior.
464+ *
465+ * @param expectContinueThresholdInBytes The threshold in bytes, or {@code null} to use the default (1048576).
466+ * @return This builder for method chaining.
467+ * @see S3Configuration#expectContinueThresholdInBytes()
468+ */
469+ Builder expectContinueThresholdInBytes (Long expectContinueThresholdInBytes );
470+
410471 Boolean useArnRegionEnabled ();
411472
412473 /**
@@ -476,6 +537,7 @@ static final class DefaultS3ServiceConfigurationBuilder implements Builder {
476537 private Boolean checksumValidationEnabled ;
477538 private Boolean chunkedEncodingEnabled ;
478539 private Boolean expectContinueEnabled ;
540+ private Long expectContinueThresholdInBytes ;
479541 private Boolean useArnRegionEnabled ;
480542 private Boolean multiRegionEnabled ;
481543 private Supplier <ProfileFile > profileFile ;
@@ -571,6 +633,21 @@ public void setExpectContinueEnabled(Boolean expectContinueEnabled) {
571633 expectContinueEnabled (expectContinueEnabled );
572634 }
573635
636+ @ Override
637+ public Long expectContinueThresholdInBytes () {
638+ return expectContinueThresholdInBytes ;
639+ }
640+
641+ @ Override
642+ public Builder expectContinueThresholdInBytes (Long expectContinueThresholdInBytes ) {
643+ this .expectContinueThresholdInBytes = expectContinueThresholdInBytes ;
644+ return this ;
645+ }
646+
647+ public void setExpectContinueThresholdInBytes (Long expectContinueThresholdInBytes ) {
648+ expectContinueThresholdInBytes (expectContinueThresholdInBytes );
649+ }
650+
574651 @ Override
575652 public Boolean useArnRegionEnabled () {
576653 return useArnRegionEnabled ;
0 commit comments