Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ private void sendFirstRequest(AsyncResponseTransformer<GetObjectResponse, GetObj

if (error != null) {
if (PresignedUrlDownloadHelper.isRangeNotSatisfiable(error)) {
resultFuture.completeExceptionally(
new PresignedUrlDownloadHelper.EmptyObjectRangeNotSatisfiableException(error));
resultFuture.completeExceptionally(error);
synchronized (subscriptionLock) {
subscription.cancel();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ public <T> CompletableFuture<T> downloadObject(
doMultipartDownload(presignedRequest, asyncResponseTransformer)
.whenComplete((result, error) -> {
Throwable cause = error instanceof CompletionException ? error.getCause() : error;
// Parallel path wraps it as EmptyObjectRangeNotSatisfiableException;
// serial path (toBytes, custom transformers) surfaces raw S3Exception.
if (cause instanceof EmptyObjectRangeNotSatisfiableException
|| isRangeNotSatisfiable(cause)) {
if (isRangeNotSatisfiable(cause)) {
log.debug(() -> "Received 416 on first request, falling back to non-range GET for empty object");
asyncPresignedUrlExtension.getObject(presignedRequest, asyncResponseTransformer)
.whenComplete((r, e) -> {
Expand Down Expand Up @@ -242,14 +239,4 @@ static boolean isRangeNotSatisfiable(Throwable error) {
Throwable cause = error instanceof CompletionException ? error.getCause() : error;
return cause instanceof S3Exception && ((S3Exception) cause).statusCode() == 416;
}

/**
* Marker exception wrapping a 416 on the first range request, signaling an empty object.
* Used to distinguish from 416 errors on subsequent requests which should propagate as failures.
*/
static class EmptyObjectRangeNotSatisfiableException extends RuntimeException {
EmptyObjectRangeNotSatisfiableException(Throwable cause) {
super("Object is empty (416 on first range request)", cause);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ private void makeRangeRequest(long partIndex,
if (error != null) {
if (partIndex == 0 && PresignedUrlDownloadHelper.isRangeNotSatisfiable(error)) {
log.debug(() -> "Received 416 on first range request, object is empty");
resultFuture.completeExceptionally(
new PresignedUrlDownloadHelper.EmptyObjectRangeNotSatisfiableException(error));
resultFuture.completeExceptionally(error);
synchronized (lock) {
subscription.cancel();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@

/**
* Request object for performing download operations using a presigned URL.
*
* <p>Presigned URLs with additional signed headers (beyond {@code Range}, {@code If-Match}, and
* {@code x-amz-checksum-mode}) are not supported. If the presigned URL was signed with {@code Range} or
* {@code If-Match}, those values must be set on this request via {@link Builder#range(String)} or
* {@link Builder#ifMatch(String)} — otherwise S3 will return a {@code SignatureDoesNotMatch} error.
* The {@code x-amz-checksum-mode} header is replayed automatically by the SDK and requires no
* additional configuration.
*/
@SdkPublicApi
public final class PresignedUrlDownloadRequest implements ToCopyableBuilder<PresignedUrlDownloadRequest.Builder,
Expand Down
Loading
Loading