diff --git a/services-custom/s3-transfer-manager/src/it/java/software/amazon/awssdk/transfer/s3/S3TransferManagerPresignedUrlDownloadIntegrationTest.java b/services-custom/s3-transfer-manager/src/it/java/software/amazon/awssdk/transfer/s3/S3TransferManagerPresignedUrlDownloadIntegrationTest.java index 9dbe520dc75d..003fddb5d5b7 100644 --- a/services-custom/s3-transfer-manager/src/it/java/software/amazon/awssdk/transfer/s3/S3TransferManagerPresignedUrlDownloadIntegrationTest.java +++ b/services-custom/s3-transfer-manager/src/it/java/software/amazon/awssdk/transfer/s3/S3TransferManagerPresignedUrlDownloadIntegrationTest.java @@ -41,8 +41,8 @@ import software.amazon.awssdk.testutils.RandomTempFile; import software.amazon.awssdk.transfer.s3.model.CompletedDownload; import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload; +import software.amazon.awssdk.transfer.s3.model.PresignedFileDownload; import software.amazon.awssdk.transfer.s3.model.Download; -import software.amazon.awssdk.transfer.s3.model.FileDownload; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadFileRequest; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest; import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener; @@ -92,7 +92,7 @@ static Stream testCases() { void downloadFileWithPresignedUrl_shouldDownloadCorrectly(S3TransferManager tm, String key, File sourceFile, int objSize) throws Exception { Path downloadPath = RandomTempFile.randomUncreatedFile().toPath(); - FileDownload download = tm.downloadFileWithPresignedUrl(createFileDownloadRequest(key, downloadPath)); + PresignedFileDownload download = tm.downloadFileWithPresignedUrl(createFileDownloadRequest(key, downloadPath)); CompletedFileDownload completed = download.completionFuture().join(); assertThat(Files.exists(downloadPath)).isTrue(); @@ -131,7 +131,7 @@ void downloadFileWithPresignedUrl_progressTracking(String tmType, S3TransferMana requestBuilder.range(range); } - FileDownload download = tm.downloadFileWithPresignedUrl( + PresignedFileDownload download = tm.downloadFileWithPresignedUrl( PresignedDownloadFileRequest.builder() .presignedUrlDownloadRequest(requestBuilder.build()) .destination(downloadPath) diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/S3TransferManager.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/S3TransferManager.java index 0f54a432a76b..aaa5916f7b1b 100644 --- a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/S3TransferManager.java +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/S3TransferManager.java @@ -43,6 +43,7 @@ import software.amazon.awssdk.transfer.s3.model.FileUpload; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadFileRequest; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest; +import software.amazon.awssdk.transfer.s3.model.PresignedFileDownload; import software.amazon.awssdk.transfer.s3.model.ResumableFileDownload; import software.amazon.awssdk.transfer.s3.model.ResumableFileUpload; import software.amazon.awssdk.transfer.s3.model.Upload; @@ -730,16 +731,16 @@ default Copy copy(Consumer copyRequestBuilder) { * LoggingTransferListener.create()) * .build(); * - * FileDownload download = transferManager.downloadFileWithPresignedUrl(request); + * PresignedFileDownload download = transferManager.downloadFileWithPresignedUrl(request); * download.completionFuture().join(); * } * * @param presignedDownloadFileRequest the presigned download file request - * @return A {@link FileDownload} that can be used to track the ongoing transfer + * @return A {@link PresignedFileDownload} that can be used to track the ongoing transfer * @see #downloadFileWithPresignedUrl(Consumer) * @see #downloadWithPresignedUrl(PresignedDownloadRequest) */ - default FileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequest presignedDownloadFileRequest) { + default PresignedFileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequest presignedDownloadFileRequest) { throw new UnsupportedOperationException(); } @@ -752,7 +753,7 @@ default FileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequest p * * @see #downloadFileWithPresignedUrl(PresignedDownloadFileRequest) */ - default FileDownload downloadFileWithPresignedUrl( + default PresignedFileDownload downloadFileWithPresignedUrl( Consumer presignedDownloadFileRequest) { return downloadFileWithPresignedUrl( PresignedDownloadFileRequest.builder().applyMutation(presignedDownloadFileRequest).build()); diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/DelegatingS3TransferManager.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/DelegatingS3TransferManager.java index 15d309552857..6907f1f00d08 100644 --- a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/DelegatingS3TransferManager.java +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/DelegatingS3TransferManager.java @@ -29,6 +29,7 @@ import software.amazon.awssdk.transfer.s3.model.FileUpload; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadFileRequest; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest; +import software.amazon.awssdk.transfer.s3.model.PresignedFileDownload; import software.amazon.awssdk.transfer.s3.model.ResumableFileDownload; import software.amazon.awssdk.transfer.s3.model.Upload; import software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest; @@ -88,7 +89,7 @@ public Copy copy(CopyRequest copyRequest) { } @Override - public FileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequest presignedDownloadFileRequest) { + public PresignedFileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequest presignedDownloadFileRequest) { return delegate.downloadFileWithPresignedUrl(presignedDownloadFileRequest); } diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/GenericS3TransferManager.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/GenericS3TransferManager.java index 8e3852a4f68a..c86a3fea12fb 100644 --- a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/GenericS3TransferManager.java +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/GenericS3TransferManager.java @@ -55,6 +55,7 @@ import software.amazon.awssdk.transfer.s3.internal.model.DefaultDownload; import software.amazon.awssdk.transfer.s3.internal.model.DefaultFileDownload; import software.amazon.awssdk.transfer.s3.internal.model.DefaultFileUpload; +import software.amazon.awssdk.transfer.s3.internal.model.DefaultPresignedFileDownload; import software.amazon.awssdk.transfer.s3.internal.model.DefaultUpload; import software.amazon.awssdk.transfer.s3.internal.progress.DefaultTransferProgress; import software.amazon.awssdk.transfer.s3.internal.progress.DefaultTransferProgressSnapshot; @@ -77,6 +78,7 @@ import software.amazon.awssdk.transfer.s3.model.FileUpload; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadFileRequest; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest; +import software.amazon.awssdk.transfer.s3.model.PresignedFileDownload; import software.amazon.awssdk.transfer.s3.model.ResumableFileDownload; import software.amazon.awssdk.transfer.s3.model.ResumableFileUpload; import software.amazon.awssdk.transfer.s3.model.Upload; @@ -600,7 +602,7 @@ public final Copy copy(CopyRequest copyRequest) { } @Override - public final FileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequest presignedDownloadFileRequest) { + public final PresignedFileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequest presignedDownloadFileRequest) { Validate.paramNotNull(presignedDownloadFileRequest, "presignedDownloadFileRequest"); AsyncResponseTransformer responseTransformer = @@ -632,11 +634,7 @@ public final FileDownload downloadFileWithPresignedUrl(PresignedDownloadFileRequ returnFuture.completeExceptionally(throwable); } - return new DefaultFileDownload(returnFuture, progressUpdater.progress(), - () -> { - throw new UnsupportedOperationException( - "Pause is not supported for presigned URL downloads"); - }, null); + return new DefaultPresignedFileDownload(returnFuture, progressUpdater.progress()); } @Override diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/model/DefaultPresignedFileDownload.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/model/DefaultPresignedFileDownload.java new file mode 100644 index 000000000000..d3dbf13b903d --- /dev/null +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/internal/model/DefaultPresignedFileDownload.java @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.transfer.s3.internal.model; + +import java.util.concurrent.CompletableFuture; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload; +import software.amazon.awssdk.transfer.s3.model.PresignedFileDownload; +import software.amazon.awssdk.transfer.s3.progress.TransferProgress; +import software.amazon.awssdk.utils.ToString; +import software.amazon.awssdk.utils.Validate; + +@SdkInternalApi +public final class DefaultPresignedFileDownload implements PresignedFileDownload { + private final CompletableFuture completionFuture; + private final TransferProgress progress; + + public DefaultPresignedFileDownload(CompletableFuture completionFuture, + TransferProgress progress) { + this.completionFuture = Validate.paramNotNull(completionFuture, "completionFuture"); + this.progress = Validate.paramNotNull(progress, "progress"); + } + + @Override + public CompletableFuture completionFuture() { + return completionFuture; + } + + @Override + public TransferProgress progress() { + return progress; + } + + @Override + public String toString() { + return ToString.builder("DefaultPresignedFileDownload") + .add("completionFuture", completionFuture) + .add("progress", progress) + .build(); + } +} diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedFileDownload.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedFileDownload.java new file mode 100644 index 000000000000..2b1d99a1944e --- /dev/null +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedFileDownload.java @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.transfer.s3.model; + +import java.util.concurrent.CompletableFuture; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; + +/** + * A download transfer of a single object from S3 using a presigned URL. + * + *

Unlike {@link FileDownload}, this type does not support pause/resume because presigned URLs + * have a limited lifetime and cannot be reliably resumed after expiration. + */ +@SdkPublicApi +@ThreadSafe +public interface PresignedFileDownload extends ObjectTransfer { + + @Override + CompletableFuture completionFuture(); +} diff --git a/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlDownloadTest.java b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlDownloadTest.java index 16de24bec605..a299e5ca41f7 100644 --- a/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlDownloadTest.java +++ b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlDownloadTest.java @@ -35,7 +35,6 @@ import software.amazon.awssdk.services.s3.presignedurl.model.PresignedUrlDownloadRequest; import software.amazon.awssdk.transfer.s3.model.CompletedDownload; import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload; -import software.amazon.awssdk.transfer.s3.model.FileDownload; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadFileRequest; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest; @@ -154,18 +153,6 @@ void downloadWithPresignedUrl_withNullRequest_shouldThrowNullPointerException() assertThatThrownBy(() -> tm.downloadWithPresignedUrl(null)) .isInstanceOf(NullPointerException.class); } - @Test - void downloadFileWithPresignedUrl_pause_shouldThrowUnsupportedOperationException() { - GetObjectResponse response = GetObjectResponse.builder().build(); - stubGetObject(CompletableFuture.completedFuture(response)); - - FileDownload download = tm.downloadFileWithPresignedUrl(fileDownloadRequest()); - download.completionFuture().join(); - - assertThatThrownBy(download::pause) - .isInstanceOf(UnsupportedOperationException.class) - .hasMessageContaining("Pause is not supported for presigned URL downloads"); - } private PresignedDownloadFileRequest fileDownloadRequest() { return PresignedDownloadFileRequest.builder() diff --git a/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlListenerWiremockTest.java b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlListenerWiremockTest.java index 8a1ee54582b3..2e9e5dbecca8 100644 --- a/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlListenerWiremockTest.java +++ b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/internal/S3TransferManagerPresignedUrlListenerWiremockTest.java @@ -48,7 +48,7 @@ import software.amazon.awssdk.testutils.RandomTempFile; import software.amazon.awssdk.transfer.s3.S3TransferManager; import software.amazon.awssdk.transfer.s3.model.Download; -import software.amazon.awssdk.transfer.s3.model.FileDownload; +import software.amazon.awssdk.transfer.s3.model.PresignedFileDownload; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadFileRequest; import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest; import software.amazon.awssdk.transfer.s3.progress.TransferListener; @@ -123,7 +123,7 @@ void presignedUrlDownload_shouldInvokeListener(boolean multipartEnabled, String } if ("toFile".equals(type)) { - FileDownload download = tm.downloadFileWithPresignedUrl( + PresignedFileDownload download = tm.downloadFileWithPresignedUrl( PresignedDownloadFileRequest.builder() .presignedUrlDownloadRequest(requestBuilder.build()) .destination(testFile.toPath()) @@ -172,7 +172,7 @@ void presignedUrlDownload_failure_shouldInvokeListener(boolean multipartEnabled, URL presignedUrl = new URL(testEndpoint + "/presigned-key?X-Amz-Algorithm=AWS4-HMAC-SHA256"); if ("toFile".equals(type)) { - FileDownload download = tm.downloadFileWithPresignedUrl( + PresignedFileDownload download = tm.downloadFileWithPresignedUrl( PresignedDownloadFileRequest.builder() .presignedUrlDownloadRequest(PresignedUrlDownloadRequest.builder() .presignedUrl(presignedUrl) @@ -222,7 +222,7 @@ void presignedUrlDownload_cancelled_shouldInvokeTransferFailed(boolean multipart URL presignedUrl = new URL(testEndpoint + "/presigned-key?X-Amz-Algorithm=AWS4-HMAC-SHA256"); if ("toFile".equals(type)) { - FileDownload download = tm.downloadFileWithPresignedUrl( + PresignedFileDownload download = tm.downloadFileWithPresignedUrl( PresignedDownloadFileRequest.builder() .presignedUrlDownloadRequest(PresignedUrlDownloadRequest.builder() .presignedUrl(presignedUrl)