/* * Copyright 2010-2023 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 com.amazonaws.services.s3; import static com.amazonaws.event.SDKProgressPublisher.publishProgress; import static com.amazonaws.internal.ResettableInputStream.newResettableInputStream; import static com.amazonaws.services.s3.model.S3DataSource.Utils.cleanupDataSource; import static com.amazonaws.util.LengthCheckInputStream.EXCLUDE_SKIPPED_BYTES; import static com.amazonaws.util.LengthCheckInputStream.INCLUDE_SKIPPED_BYTES; import static com.amazonaws.util.Throwables.failure; import static com.amazonaws.util.ValidationUtils.assertNotNull; import static com.amazonaws.util.ValidationUtils.assertStringNotEmpty; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.AmazonServiceException.ErrorType; import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.AmazonWebServiceRequest; import com.amazonaws.AmazonWebServiceResponse; import com.amazonaws.ClientConfiguration; import com.amazonaws.DefaultRequest; import com.amazonaws.HttpMethod; import com.amazonaws.Protocol; import com.amazonaws.Request; import com.amazonaws.RequestConfig; import com.amazonaws.ResetException; import com.amazonaws.Response; import com.amazonaws.SDKGlobalConfiguration; import com.amazonaws.SdkClientException; import com.amazonaws.annotation.SdkInternalApi; import com.amazonaws.annotation.SdkTestInternalApi; import com.amazonaws.annotation.ThreadSafe; import com.amazonaws.arn.Arn; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.Presigner; import com.amazonaws.auth.ServiceAwareSigner; import com.amazonaws.auth.Signer; import com.amazonaws.auth.SignerFactory; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.event.ProgressEventType; import com.amazonaws.event.ProgressInputStream; import com.amazonaws.event.ProgressListener; import com.amazonaws.handlers.HandlerChainFactory; import com.amazonaws.handlers.HandlerContextKey; import com.amazonaws.handlers.RequestHandler2; import com.amazonaws.http.ExecutionContext; import com.amazonaws.http.HttpMethodName; import com.amazonaws.http.HttpResponseHandler; import com.amazonaws.internal.AmazonWebServiceRequestAdapter; import com.amazonaws.internal.DefaultServiceEndpointBuilder; import com.amazonaws.internal.IdentityEndpointBuilder; import com.amazonaws.internal.ReleasableInputStream; import com.amazonaws.internal.ResettableInputStream; import com.amazonaws.internal.SdkFilterInputStream; import com.amazonaws.internal.ServiceEndpointBuilder; import com.amazonaws.internal.StaticCredentialsProvider; import com.amazonaws.internal.auth.NoOpSignerProvider; import com.amazonaws.internal.auth.SignerProvider; import com.amazonaws.metrics.AwsSdkMetrics; import com.amazonaws.metrics.RequestMetricCollector; import com.amazonaws.regions.RegionUtils; import com.amazonaws.regions.Regions; import com.amazonaws.retry.PredefinedRetryPolicies; import com.amazonaws.retry.RetryPolicy; import com.amazonaws.services.s3.internal.AWSS3V4Signer; import com.amazonaws.services.s3.internal.BucketNameUtils; import com.amazonaws.services.s3.internal.CompleteMultipartUploadRetryCondition; import com.amazonaws.services.s3.internal.Constants; import com.amazonaws.services.s3.internal.DeleteObjectTaggingHeaderHandler; import com.amazonaws.services.s3.internal.DeleteObjectsResponse; import com.amazonaws.services.s3.internal.DigestValidationInputStream; import com.amazonaws.services.s3.internal.DualstackEndpointBuilder; import com.amazonaws.services.s3.internal.GetObjectTaggingResponseHeaderHandler; import com.amazonaws.services.s3.internal.InitiateMultipartUploadHeaderHandler; import com.amazonaws.services.s3.internal.InputSubstream; import com.amazonaws.services.s3.internal.ListPartsHeaderHandler; import com.amazonaws.services.s3.internal.MD5DigestCalculatingInputStream; import com.amazonaws.services.s3.internal.Mimetypes; import com.amazonaws.services.s3.internal.MultiFileOutputStream; import com.amazonaws.services.s3.internal.ObjectExpirationHeaderHandler; import com.amazonaws.services.s3.internal.RegionalEndpointsOptionResolver; import com.amazonaws.services.s3.internal.ResponseHeaderHandlerChain; import com.amazonaws.services.s3.internal.S3AbortableInputStream; import com.amazonaws.services.s3.internal.S3AccessPointBuilder; import com.amazonaws.services.s3.internal.S3ObjectLambdaOperationEndpointBuilder; import com.amazonaws.services.s3.internal.S3ObjectLambdaEndpointBuilder; import com.amazonaws.services.s3.internal.S3ErrorResponseHandler; import com.amazonaws.services.s3.internal.S3MetadataResponseHandler; import com.amazonaws.services.s3.internal.S3ObjectResponseHandler; import com.amazonaws.services.s3.internal.S3OutpostAccessPointBuilder; import com.amazonaws.services.s3.internal.S3OutpostResource; import com.amazonaws.services.s3.internal.S3QueryStringSigner; import com.amazonaws.services.s3.internal.S3RequestEndpointResolver; import com.amazonaws.services.s3.internal.S3RequesterChargedHeaderHandler; import com.amazonaws.services.s3.internal.S3RestoreOutputPathHeaderHandler; import com.amazonaws.services.s3.internal.S3Signer; import com.amazonaws.services.s3.internal.S3StringResponseHandler; import com.amazonaws.services.s3.internal.S3V4AuthErrorRetryStrategy; import com.amazonaws.services.s3.internal.S3VersionHeaderHandler; import com.amazonaws.services.s3.internal.S3XmlResponseHandler; import com.amazonaws.services.s3.internal.ServerSideEncryptionHeaderHandler; import com.amazonaws.services.s3.internal.ServiceUtils; import com.amazonaws.services.s3.internal.SetObjectTaggingResponseHeaderHandler; import com.amazonaws.services.s3.internal.SkipMd5CheckStrategy; import com.amazonaws.services.s3.internal.UploadObjectStrategy; import com.amazonaws.services.s3.internal.UseArnRegionResolver; import com.amazonaws.services.s3.internal.XmlWriter; import com.amazonaws.services.s3.internal.auth.S3SignerProvider; import com.amazonaws.services.s3.metrics.S3ServiceMetric; import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; import com.amazonaws.services.s3.model.AccessControlList; import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.s3.model.BucketAccelerateConfiguration; import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; import com.amazonaws.services.s3.model.BucketLoggingConfiguration; import com.amazonaws.services.s3.model.BucketNotificationConfiguration; import com.amazonaws.services.s3.model.BucketPolicy; import com.amazonaws.services.s3.model.BucketReplicationConfiguration; import com.amazonaws.services.s3.model.BucketTaggingConfiguration; import com.amazonaws.services.s3.model.BucketVersioningConfiguration; import com.amazonaws.services.s3.model.BucketWebsiteConfiguration; import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; import com.amazonaws.services.s3.model.CopyObjectRequest; import com.amazonaws.services.s3.model.CopyObjectResult; import com.amazonaws.services.s3.model.CopyPartRequest; import com.amazonaws.services.s3.model.CopyPartResult; import com.amazonaws.services.s3.model.CreateBucketRequest; import com.amazonaws.services.s3.model.DeleteBucketAnalyticsConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketAnalyticsConfigurationResult; import com.amazonaws.services.s3.model.DeleteBucketCrossOriginConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketEncryptionRequest; import com.amazonaws.services.s3.model.DeleteBucketEncryptionResult; import com.amazonaws.services.s3.model.DeleteBucketIntelligentTieringConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketIntelligentTieringConfigurationResult; import com.amazonaws.services.s3.model.DeleteBucketInventoryConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketInventoryConfigurationResult; import com.amazonaws.services.s3.model.DeleteBucketLifecycleConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketMetricsConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketMetricsConfigurationResult; import com.amazonaws.services.s3.model.DeleteBucketOwnershipControlsRequest; import com.amazonaws.services.s3.model.DeleteBucketOwnershipControlsResult; import com.amazonaws.services.s3.model.DeleteBucketPolicyRequest; import com.amazonaws.services.s3.model.DeleteBucketReplicationConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketRequest; import com.amazonaws.services.s3.model.DeleteBucketTaggingConfigurationRequest; import com.amazonaws.services.s3.model.DeleteBucketWebsiteConfigurationRequest; import com.amazonaws.services.s3.model.DeleteObjectRequest; import com.amazonaws.services.s3.model.DeleteObjectTaggingRequest; import com.amazonaws.services.s3.model.DeleteObjectTaggingResult; import com.amazonaws.services.s3.model.DeleteObjectsRequest; import com.amazonaws.services.s3.model.DeleteObjectsResult; import com.amazonaws.services.s3.model.DeletePublicAccessBlockRequest; import com.amazonaws.services.s3.model.DeletePublicAccessBlockResult; import com.amazonaws.services.s3.model.DeleteVersionRequest; import com.amazonaws.services.s3.model.ExpectedBucketOwnerRequest; import com.amazonaws.services.s3.model.ExpectedSourceBucketOwnerRequest; import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; import com.amazonaws.services.s3.model.GenericBucketRequest; import com.amazonaws.services.s3.model.GetBucketAccelerateConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketAclRequest; import com.amazonaws.services.s3.model.GetBucketAnalyticsConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketAnalyticsConfigurationResult; import com.amazonaws.services.s3.model.GetBucketCrossOriginConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketEncryptionRequest; import com.amazonaws.services.s3.model.GetBucketEncryptionResult; import com.amazonaws.services.s3.model.GetBucketIntelligentTieringConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketIntelligentTieringConfigurationResult; import com.amazonaws.services.s3.model.GetBucketInventoryConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketInventoryConfigurationResult; import com.amazonaws.services.s3.model.GetBucketLifecycleConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketLocationRequest; import com.amazonaws.services.s3.model.GetBucketLoggingConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketMetricsConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketMetricsConfigurationResult; import com.amazonaws.services.s3.model.GetBucketNotificationConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketOwnershipControlsRequest; import com.amazonaws.services.s3.model.GetBucketOwnershipControlsResult; import com.amazonaws.services.s3.model.GetBucketPolicyRequest; import com.amazonaws.services.s3.model.GetBucketPolicyStatusRequest; import com.amazonaws.services.s3.model.GetBucketPolicyStatusResult; import com.amazonaws.services.s3.model.GetBucketReplicationConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketTaggingConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketVersioningConfigurationRequest; import com.amazonaws.services.s3.model.GetBucketWebsiteConfigurationRequest; import com.amazonaws.services.s3.model.GetObjectAclRequest; import com.amazonaws.services.s3.model.GetObjectLegalHoldRequest; import com.amazonaws.services.s3.model.GetObjectLegalHoldResult; import com.amazonaws.services.s3.model.GetObjectLockConfigurationRequest; import com.amazonaws.services.s3.model.GetObjectLockConfigurationResult; import com.amazonaws.services.s3.model.GetObjectMetadataRequest; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.GetObjectRetentionRequest; import com.amazonaws.services.s3.model.GetObjectRetentionResult; import com.amazonaws.services.s3.model.GetObjectTaggingRequest; import com.amazonaws.services.s3.model.GetObjectTaggingResult; import com.amazonaws.services.s3.model.GetPublicAccessBlockRequest; import com.amazonaws.services.s3.model.GetPublicAccessBlockResult; import com.amazonaws.services.s3.model.GetRequestPaymentConfigurationRequest; import com.amazonaws.services.s3.model.GetS3AccountOwnerRequest; import com.amazonaws.services.s3.model.Grant; import com.amazonaws.services.s3.model.Grantee; import com.amazonaws.services.s3.model.GroupGrantee; import com.amazonaws.services.s3.model.HeadBucketRequest; import com.amazonaws.services.s3.model.HeadBucketResult; import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; import com.amazonaws.services.s3.model.ListBucketAnalyticsConfigurationsRequest; import com.amazonaws.services.s3.model.ListBucketAnalyticsConfigurationsResult; import com.amazonaws.services.s3.model.ListBucketIntelligentTieringConfigurationsRequest; import com.amazonaws.services.s3.model.ListBucketIntelligentTieringConfigurationsResult; import com.amazonaws.services.s3.model.ListBucketInventoryConfigurationsRequest; import com.amazonaws.services.s3.model.ListBucketInventoryConfigurationsResult; import com.amazonaws.services.s3.model.ListBucketMetricsConfigurationsRequest; import com.amazonaws.services.s3.model.ListBucketMetricsConfigurationsResult; import com.amazonaws.services.s3.model.ListBucketsRequest; import com.amazonaws.services.s3.model.ListMultipartUploadsRequest; import com.amazonaws.services.s3.model.ListNextBatchOfObjectsRequest; import com.amazonaws.services.s3.model.ListNextBatchOfVersionsRequest; import com.amazonaws.services.s3.model.ListObjectsRequest; import com.amazonaws.services.s3.model.ListObjectsV2Request; import com.amazonaws.services.s3.model.ListObjectsV2Result; import com.amazonaws.services.s3.model.ListPartsRequest; import com.amazonaws.services.s3.model.ListVersionsRequest; import com.amazonaws.services.s3.model.MultiFactorAuthentication; import com.amazonaws.services.s3.model.MultiObjectDeleteException; import com.amazonaws.services.s3.model.MultipartUploadListing; import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.ObjectTagging; import com.amazonaws.services.s3.model.Owner; import com.amazonaws.services.s3.model.PartETag; import com.amazonaws.services.s3.model.PartListing; import com.amazonaws.services.s3.model.Permission; import com.amazonaws.services.s3.model.PresignedUrlDownloadRequest; import com.amazonaws.services.s3.model.PresignedUrlDownloadResult; import com.amazonaws.services.s3.model.PresignedUrlUploadRequest; import com.amazonaws.services.s3.model.PresignedUrlUploadResult; import com.amazonaws.services.s3.model.PublicAccessBlockConfiguration; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.PutObjectResult; import com.amazonaws.services.s3.model.Region; import com.amazonaws.services.s3.model.RequestPaymentConfiguration; import com.amazonaws.services.s3.model.RequestPaymentConfiguration.Payer; import com.amazonaws.services.s3.model.ResponseHeaderOverrides; import com.amazonaws.services.s3.model.RestoreObjectRequest; import com.amazonaws.services.s3.model.RestoreObjectResult; import com.amazonaws.services.s3.model.RestoreRequestType; import com.amazonaws.services.s3.model.S3AccelerateUnsupported; import com.amazonaws.services.s3.model.S3DataSource; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectInputStream; import com.amazonaws.services.s3.model.SSEAwsKeyManagementParams; import com.amazonaws.services.s3.model.SSEAwsKeyManagementParamsProvider; import com.amazonaws.services.s3.model.SSECustomerKey; import com.amazonaws.services.s3.model.SSECustomerKeyProvider; import com.amazonaws.services.s3.model.SelectObjectContentEventStream; import com.amazonaws.services.s3.model.SelectObjectContentRequest; import com.amazonaws.services.s3.model.SelectObjectContentResult; import com.amazonaws.services.s3.model.ServerSideEncryptionConfiguration; import com.amazonaws.services.s3.model.SetBucketAccelerateConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketAclRequest; import com.amazonaws.services.s3.model.SetBucketAnalyticsConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketAnalyticsConfigurationResult; import com.amazonaws.services.s3.model.SetBucketCrossOriginConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketEncryptionRequest; import com.amazonaws.services.s3.model.SetBucketEncryptionResult; import com.amazonaws.services.s3.model.SetBucketIntelligentTieringConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketIntelligentTieringConfigurationResult; import com.amazonaws.services.s3.model.SetBucketInventoryConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketInventoryConfigurationResult; import com.amazonaws.services.s3.model.SetBucketLifecycleConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketLoggingConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketMetricsConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketMetricsConfigurationResult; import com.amazonaws.services.s3.model.SetBucketNotificationConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketOwnershipControlsRequest; import com.amazonaws.services.s3.model.SetBucketOwnershipControlsResult; import com.amazonaws.services.s3.model.SetBucketPolicyRequest; import com.amazonaws.services.s3.model.SetBucketReplicationConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketTaggingConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketVersioningConfigurationRequest; import com.amazonaws.services.s3.model.SetBucketWebsiteConfigurationRequest; import com.amazonaws.services.s3.model.SetObjectAclRequest; import com.amazonaws.services.s3.model.SetObjectLegalHoldRequest; import com.amazonaws.services.s3.model.SetObjectLegalHoldResult; import com.amazonaws.services.s3.model.SetObjectLockConfigurationRequest; import com.amazonaws.services.s3.model.SetObjectLockConfigurationResult; import com.amazonaws.services.s3.model.SetObjectRetentionRequest; import com.amazonaws.services.s3.model.SetObjectRetentionResult; import com.amazonaws.services.s3.model.SetObjectTaggingRequest; import com.amazonaws.services.s3.model.SetObjectTaggingResult; import com.amazonaws.services.s3.model.SetPublicAccessBlockRequest; import com.amazonaws.services.s3.model.SetPublicAccessBlockResult; import com.amazonaws.services.s3.model.SetRequestPaymentConfigurationRequest; import com.amazonaws.services.s3.model.StorageClass; import com.amazonaws.services.s3.model.Tag; import com.amazonaws.services.s3.model.UploadObjectRequest; import com.amazonaws.services.s3.model.UploadPartRequest; import com.amazonaws.services.s3.model.UploadPartResult; import com.amazonaws.services.s3.model.VersionListing; import com.amazonaws.services.s3.model.WriteGetObjectResponseRequest; import com.amazonaws.services.s3.model.WriteGetObjectResponseResult; import com.amazonaws.services.s3.model.analytics.AnalyticsConfiguration; import com.amazonaws.services.s3.model.intelligenttiering.IntelligentTieringConfiguration; import com.amazonaws.services.s3.model.inventory.InventoryConfiguration; import com.amazonaws.services.s3.model.metrics.MetricsConfiguration; import com.amazonaws.services.s3.model.ownership.OwnershipControls; import com.amazonaws.services.s3.model.transform.AclXmlFactory; import com.amazonaws.services.s3.model.transform.BucketConfigurationXmlFactory; import com.amazonaws.services.s3.model.transform.BucketNotificationConfigurationStaxUnmarshaller; import com.amazonaws.services.s3.model.transform.GetBucketEncryptionStaxUnmarshaller; import com.amazonaws.services.s3.model.transform.GetBucketPolicyStatusStaxUnmarshaller; import com.amazonaws.services.s3.model.transform.GetPublicAccessBlockStaxUnmarshaller; import com.amazonaws.services.s3.model.transform.HeadBucketResultHandler; import com.amazonaws.services.s3.model.transform.MultiObjectDeleteXmlFactory; import com.amazonaws.services.s3.model.transform.ObjectLockConfigurationXmlFactory; import com.amazonaws.services.s3.model.transform.ObjectLockLegalHoldXmlFactory; import com.amazonaws.services.s3.model.transform.ObjectLockRetentionXmlFactory; import com.amazonaws.services.s3.model.transform.ObjectTaggingXmlFactory; import com.amazonaws.services.s3.model.transform.RequestPaymentConfigurationXmlFactory; import com.amazonaws.services.s3.model.transform.RequestXmlFactory; import com.amazonaws.services.s3.model.transform.Unmarshallers; import com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.CompleteMultipartUploadHandler; import com.amazonaws.services.s3.model.transform.XmlResponsesSaxParser.CopyObjectResultHandler; import com.amazonaws.services.s3.request.S3HandlerContextKeys; import com.amazonaws.services.s3.waiters.AmazonS3Waiters; import com.amazonaws.transform.Unmarshaller; import com.amazonaws.util.AWSRequestMetrics; import com.amazonaws.util.AWSRequestMetrics.Field; import com.amazonaws.util.AwsHostNameUtils; import com.amazonaws.util.Base16; import com.amazonaws.util.Base64; import com.amazonaws.util.BinaryUtils; import com.amazonaws.util.CredentialUtils; import com.amazonaws.util.DateUtils; import com.amazonaws.util.HostnameValidator; import com.amazonaws.util.IOUtils; import com.amazonaws.util.LengthCheckInputStream; import com.amazonaws.util.Md5Utils; import com.amazonaws.util.RuntimeHttpUtils; import com.amazonaws.util.SdkHttpUtils; import com.amazonaws.util.ServiceClientHolderInputStream; import com.amazonaws.util.StringUtils; import com.amazonaws.util.UriResourcePathUtils; import com.amazonaws.util.ValidationUtils; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.regex.Matcher; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.ContentType; /** *
* Provides the client for accessing the Amazon S3 web service. *
** Amazon S3 provides storage for the Internet, * and is designed to make web-scale computing easier for developers. *
** The Amazon S3 Java Client provides a simple interface that can be * used to store and retrieve any amount of data, at any time, * from anywhere on the web. It gives any developer access to the same * highly scalable, reliable, secure, fast, inexpensive infrastructure * that Amazon uses to run its own global network of web sites. * The service aims to maximize benefits of scale and to pass those * benefits on to developers. *
** For more information about Amazon S3, please see * * http://aws.amazon.com/s3 *
*/ @ThreadSafe public class AmazonS3Client extends AmazonWebServiceClient implements AmazonS3 { public static final String S3_SERVICE_NAME = "s3"; private static final String S3_SIGNER = "S3SignerType"; private static final String S3_V4_SIGNER = "AWSS3V4SignerType"; private static final String SERVICE_ID = "S3"; private static final String AWS_PARTITION_KEY = "aws"; private static final String S3_OUTPOSTS_NAME = "s3-outposts"; private static final String S3_OBJECT_LAMBDAS_NAME = "s3-object-lambda"; protected static final AmazonS3ClientConfigurationFactory configFactory = new AmazonS3ClientConfigurationFactory(); /** Shared logger for client events */ private static Log log = LogFactory.getLog(AmazonS3Client.class); static { // Enable S3 specific predefined request metrics. AwsSdkMetrics.addAll(Arrays.asList(S3ServiceMetric.values())); // Register S3-specific signers. SignerFactory.registerSigner(S3_SIGNER, S3Signer.class); SignerFactory.registerSigner(S3_V4_SIGNER, AWSS3V4Signer.class); } private volatile AmazonS3Waiters waiters; /** Provider for Amazon Web Services credentials. */ protected final AWSCredentialsProvider awsCredentialsProvider; /** Responsible for handling error responses from all S3 service calls. */ protected final S3ErrorResponseHandler errorResponseHandler; /** Shared response handler for operations with no response. */ private final S3XmlResponseHandler* If no credentials are found in the chain, this client will attempt to * work in an anonymous mode where requests aren't signed. Only a subset of * the Amazon S3 API will work with anonymous (i.e. unsigned) requests, * but this can prove useful in some situations. For example: *
* You can force the client to operate in an anonymous mode, and skip the credentials
* provider chain, by passing in null
for the credentials.
*
* If no credentials are found in the chain, this client will attempt to * work in an anonymous mode where requests aren't signed. Only a subset of * the Amazon S3 API will work with anonymous (i.e. unsigned) * requests, but this can prove useful in some situations. For example: *
* You can force the client to operate in an anonymous mode, and skip the
* credentials provider chain, by passing in null
for the
* credentials.
*
* Override the default S3 client options for this client. Also set the * endpoint to s3-accelerate if such is specified in the S3 client options. *
* * @param clientOptions * The S3 client options to use. */ @Override public synchronized void setS3ClientOptions(S3ClientOptions clientOptions) { checkMutability(); this.clientOptions = new S3ClientOptions(clientOptions); } /** * S3 uses wildcard certificates so we have to disable strict hostname verification when using * SSL. */ @Override protected boolean useStrictHostNameVerification() { return false; } @Override public VersionListing listNextBatchOfVersions(VersionListing previousVersionListing) throws SdkClientException, AmazonServiceException { return listNextBatchOfVersions(new ListNextBatchOfVersionsRequest(previousVersionListing)); } @Override public VersionListing listNextBatchOfVersions(ListNextBatchOfVersionsRequest listNextBatchOfVersionsRequest) { listNextBatchOfVersionsRequest = beforeClientExecution(listNextBatchOfVersionsRequest); rejectNull(listNextBatchOfVersionsRequest, "The request object parameter must be specified when listing the next batch of versions in a bucket"); VersionListing previousVersionListing = listNextBatchOfVersionsRequest.getPreviousVersionListing(); if (!previousVersionListing.isTruncated()) { VersionListing emptyListing = new VersionListing(); emptyListing.setBucketName(previousVersionListing.getBucketName()); emptyListing.setDelimiter(previousVersionListing.getDelimiter()); emptyListing.setKeyMarker(previousVersionListing.getNextKeyMarker()); emptyListing.setVersionIdMarker(previousVersionListing.getNextVersionIdMarker()); emptyListing.setMaxKeys(previousVersionListing.getMaxKeys()); emptyListing.setPrefix(previousVersionListing.getPrefix()); emptyListing.setEncodingType(previousVersionListing.getEncodingType()); emptyListing.setTruncated(false); return emptyListing; } return listVersions(listNextBatchOfVersionsRequest.toListVersionsRequest()); } @Override public VersionListing listVersions(String bucketName, String prefix) throws SdkClientException, AmazonServiceException { return listVersions(new ListVersionsRequest(bucketName, prefix, null, null, null, null)); } @Override public VersionListing listVersions(String bucketName, String prefix, String keyMarker, String versionIdMarker, String delimiter, Integer maxKeys) throws SdkClientException, AmazonServiceException { ListVersionsRequest request = new ListVersionsRequest() .withBucketName(bucketName) .withPrefix(prefix) .withDelimiter(delimiter) .withKeyMarker(keyMarker) .withVersionIdMarker(versionIdMarker) .withMaxResults(maxKeys); return listVersions(request); } @Override public VersionListing listVersions(ListVersionsRequest listVersionsRequest) throws SdkClientException, AmazonServiceException { listVersionsRequest = beforeClientExecution(listVersionsRequest); rejectNull(listVersionsRequest.getBucketName(), "The bucket name parameter must be specified when listing versions in a bucket"); /** * This flag shows whether we need to url decode S3 key names. This flag is enabled * only when the customers don't explicitly call {@link listVersionsRequest#setEncodingType(String)}, * otherwise, it will be disabled for maintaining backwards compatibility. */ final boolean shouldSDKDecodeResponse = listVersionsRequest.getEncodingType() == null; RequestFor information about maximum and minimum part sizes and other multipart upload specifications, * see Multipart upload limits * in the Amazon S3 User Guide.
*
* If constraints are specified in the CopyPartRequest
* (e.g.
* {@link CopyPartRequest#setMatchingETagConstraints(List)})
* and are not satisfied when Amazon S3 receives the
* request, this method returns null
.
* This method returns a non-null result under all other
* circumstances.
*
null
if
* constraints were specified that weren't met when Amazon S3 attempted
* to copy the object.
*
* @throws SdkClientException
* If any errors are encountered in the client while making the
* request or handling the response.
* @throws AmazonServiceException
* If any errors occurred in Amazon S3 while processing the
* request.
*
* @see AmazonS3#copyObject(CopyObjectRequest)
* @see AmazonS3#initiateMultipartUpload(InitiateMultipartUploadRequest)
*/
@Override
public CopyPartResult copyPart(CopyPartRequest copyPartRequest) {
copyPartRequest = beforeClientExecution(copyPartRequest);
rejectNull(copyPartRequest.getSourceBucketName(),
"The source bucket name must be specified when copying a part");
rejectNull(copyPartRequest.getSourceKey(),
"The source object key must be specified when copying a part");
rejectNull(copyPartRequest.getDestinationBucketName(),
"The destination bucket name must be specified when copying a part");
rejectNull(copyPartRequest.getUploadId(),
"The upload id must be specified when copying a part");
rejectNull(copyPartRequest.getDestinationKey(),
"The destination object key must be specified when copying a part");
rejectNull(copyPartRequest.getPartNumber(),
"The part number must be specified when copying a part");
String destinationKey = copyPartRequest.getDestinationKey();
String destinationBucketName = copyPartRequest.getDestinationBucketName();
Request
* Asserts that the specified parameter value is not null
and if it is,
* throws an IllegalArgumentException
with the specified error message.
*
* Gets the Amazon S3 {@link AccessControlList} (ACL) for the specified resource. * (bucket if only the bucketName parameter is specified, otherwise the object with the * specified key in the bucket). *
* * @param bucketName * The name of the bucket whose ACL should be returned if the key * parameter is not specified, otherwise the bucket containing * the specified key. * @param key * The object key whose ACL should be retrieve. If not specified, * the bucket's ACL is returned. * @param versionId * The version ID of the object version whose ACL is being * retrieved. * @param originalRequest * The original, user facing request object. * * @return The S3 ACL for the specified resource. */ private AccessControlList getAcl(String bucketName, String key, String versionId, boolean isRequesterPays, AmazonWebServiceRequest originalRequest) { if (originalRequest == null) originalRequest = new GenericBucketRequest(bucketName); RequestReturns true if the region required for signing could not be computed from the client or the request.
** This is the case when the standard endpoint is in use and neither an explicit region nor a signer override * have been provided by the user. *
*/ private boolean noExplicitRegionProvided(final Request> request) { return isStandardEndpoint(request.getEndpoint()) && getSignerRegion() == null; } private boolean isStandardEndpoint(URI endpoint) { return endpoint.getHost().endsWith(Constants.S3_HOSTNAME); } /** * Pre-signs the specified request, using a signature query-string * parameter. * * @param request * The request to sign. * @param methodName * The HTTP method (GET, PUT, DELETE, HEAD) for the specified * request. * @param bucketName * The name of the bucket involved in the request. If the request * is not an operation on a bucket this parameter should be null. * @param key * The object key involved in the request. If the request is not * an operation on an object, this parameter should be null. * @param expiration * The time at which the signed request is no longer valid, and * will stop working. * @param subResource * The optional sub-resource being requested as part of the * request (e.g. "location", "acl", "logging", or "torrent"). */ protected* Populates the specified request object with the appropriate headers from * the {@link ObjectMetadata} object. *
* * @param request * The request to populate with headers. * @param metadata * The metadata containing the header information to include in * the request. */ protected static void populateRequestMetadata(Request> request, ObjectMetadata metadata) { Map* Populate the specified request with {@link Constants#REQUESTER_PAYS} to header {@link Headers#REQUESTER_PAYS_HEADER}, * if isRequesterPays is true. *
* * @param request * The specified request to populate. * @param isRequesterPays * The flag whether to populate the header or not. */ protected static void populateRequesterPaysHeader(Request> request, boolean isRequesterPays) { if (isRequesterPays) { request.addHeader(Headers.REQUESTER_PAYS_HEADER, Constants.REQUESTER_PAYS); } } /** ** Populates the specified request with the specified Multi-Factor * Authentication (MFA) details. This includes the MFA header with device serial * number and generated token. Since all requests which include the MFA * header must be sent over HTTPS, this operation also configures the request object to * use HTTPS instead of HTTP. *
* * @param request * The request to populate. * @param mfa * The Multi-Factor Authentication information. */ private void populateRequestWithMfaDetails(Request> request, MultiFactorAuthentication mfa) { if (mfa == null) return; String endpoint = request.getEndpoint().toString(); if (endpoint.startsWith("http://")) { String httpsEndpoint = endpoint.replace("http://", "https://"); request.setEndpoint(URI.create(httpsEndpoint)); log.info("Overriding current endpoint to use HTTPS " + "as required by S3 for requests containing an MFA header"); } request.addHeader(Headers.S3_MFA, mfa.getDeviceSerialNumber() + " " + mfa.getToken()); } /** *
* Populates the specified request with the numerous options available in
* CopyObjectRequest
.
*
CopyObjectRequest
object.
* @param copyObjectRequest
* The object containing all the options for copying an object in
* Amazon S3.
*/
private void populateRequestWithCopyObjectParameters(Request extends AmazonWebServiceRequest> request, CopyObjectRequest copyObjectRequest) {
String copySourceHeader = assembleCopySourceHeader(copyObjectRequest.getSourceBucketName(),
copyObjectRequest.getSourceKey(),
copyObjectRequest.getSourceVersionId());
request.addHeader("x-amz-copy-source", copySourceHeader);
addDateHeader(request, Headers.COPY_SOURCE_IF_MODIFIED_SINCE,
copyObjectRequest.getModifiedSinceConstraint());
addDateHeader(request, Headers.COPY_SOURCE_IF_UNMODIFIED_SINCE,
copyObjectRequest.getUnmodifiedSinceConstraint());
addStringListHeader(request, Headers.COPY_SOURCE_IF_MATCH,
copyObjectRequest.getMatchingETagConstraints());
addStringListHeader(request, Headers.COPY_SOURCE_IF_NO_MATCH,
copyObjectRequest.getNonmatchingETagConstraints());
if (copyObjectRequest.getAccessControlList() != null) {
addAclHeaders(request, copyObjectRequest.getAccessControlList());
} else if (copyObjectRequest.getCannedAccessControlList() != null) {
request.addHeader(Headers.S3_CANNED_ACL,
copyObjectRequest.getCannedAccessControlList().toString());
}
if (copyObjectRequest.getStorageClass() != null) {
request.addHeader(Headers.STORAGE_CLASS, copyObjectRequest.getStorageClass());
}
if (copyObjectRequest.getRedirectLocation() != null) {
request.addHeader(Headers.REDIRECT_LOCATION, copyObjectRequest.getRedirectLocation());
}
populateRequesterPaysHeader(request, copyObjectRequest.isRequesterPays());
ObjectMetadata newObjectMetadata = copyObjectRequest.getNewObjectMetadata();
if (copyObjectRequest.getMetadataDirective() != null) {
request.addHeader(Headers.METADATA_DIRECTIVE, copyObjectRequest.getMetadataDirective());
} else if (newObjectMetadata != null) {
request.addHeader(Headers.METADATA_DIRECTIVE, "REPLACE");
}
if (newObjectMetadata != null) {
populateRequestMetadata(request, newObjectMetadata);
}
ObjectTagging newObjectTagging = copyObjectRequest.getNewObjectTagging();
if (newObjectTagging != null) {
request.addHeader(Headers.TAGGING_DIRECTIVE, "REPLACE");
request.addHeader(Headers.S3_TAGGING, urlEncodeTags(newObjectTagging));
}
// Populate the SSE-C parameters for the destination object
populateSourceSSE_C(request, copyObjectRequest.getSourceSSECustomerKey());
populateSSE_C(request, copyObjectRequest.getDestinationSSECustomerKey());
}
/**
*
* Populates the specified request with the numerous options available in
* CopyObjectRequest
.
*
CopyPartRequest
object.
* @param copyPartRequest
* The object containing all the options for copying an object in
* Amazon S3.
*/
private void populateRequestWithCopyPartParameters(Request> request, CopyPartRequest copyPartRequest) {
String copySourceHeader = assembleCopySourceHeader(copyPartRequest.getSourceBucketName(),
copyPartRequest.getSourceKey(),
copyPartRequest.getSourceVersionId());
request.addHeader("x-amz-copy-source", copySourceHeader);
addDateHeader(request, Headers.COPY_SOURCE_IF_MODIFIED_SINCE,
copyPartRequest.getModifiedSinceConstraint());
addDateHeader(request, Headers.COPY_SOURCE_IF_UNMODIFIED_SINCE,
copyPartRequest.getUnmodifiedSinceConstraint());
addStringListHeader(request, Headers.COPY_SOURCE_IF_MATCH,
copyPartRequest.getMatchingETagConstraints());
addStringListHeader(request, Headers.COPY_SOURCE_IF_NO_MATCH,
copyPartRequest.getNonmatchingETagConstraints());
if ( copyPartRequest.getFirstByte() != null && copyPartRequest.getLastByte() != null ) {
String range = "bytes=" + copyPartRequest.getFirstByte() + "-" + copyPartRequest.getLastByte();
request.addHeader(Headers.COPY_PART_RANGE, range);
}
// Populate the SSE-C parameters for the destination object
populateSourceSSE_C(request, copyPartRequest.getSourceSSECustomerKey());
populateSSE_C(request, copyPartRequest.getDestinationSSECustomerKey());
}
/**
* Populates the specified request header with Content-MD5.
* @param request The request to populate with Content-MD5 header.
* @param content Content for which MD5Hash is calculated.
*/
private void populateRequestHeaderWithMd5(Request> request, byte[] content) {
try {
byte[] md5 = Md5Utils.computeMD5Hash(content);
String md5Base64 = BinaryUtils.toBase64(md5);
request.addHeader("Content-MD5", md5Base64);
} catch ( Exception e ) {
throw new SdkClientException("Couldn't compute md5 sum", e);
}
}
/**
* Assemble copy source header (x-amz-copy-source) from copy source bucket name, object key, and version ID.
*
* @param sourceBucketName copy source bucket name, can either be source bucket name or source access point ARN
* @param sourceObjectKey copy source object key
* @param sourceVersionId copy source version ID, optional.
* @return copy source header (x-amz-copy-source)
*/
private String assembleCopySourceHeader(String sourceBucketName, String sourceObjectKey, String sourceVersionId) {
if (sourceBucketName == null) {
throw new IllegalArgumentException("Copy source bucket name should not be null");
}
if (sourceObjectKey == null) {
throw new IllegalArgumentException("Copy source object key should not be null");
}
String copySourceHeader;
if (isArn(sourceBucketName)) {
// The source bucket name appears to be ARN. Parse it as S3 access point ARN and form
// object-via-access-point copy source header.
Arn resourceArn = Arn.fromString(sourceBucketName);
S3Resource s3Resource;
try {
s3Resource = S3ArnConverter.getInstance().convertArn(resourceArn);
} catch (RuntimeException e) {
throw new IllegalArgumentException("An ARN was passed as a bucket parameter to an S3 operation, "
+ "however it does not appear to be a valid S3 access point ARN.", e);
}
if (!S3ResourceType.ACCESS_POINT.toString().equals(s3Resource.getType())) {
throw new IllegalArgumentException("An ARN was passed as a bucket parameter to an S3 operation, "
+ "however it does not appear to be a valid S3 access point ARN.");
}
copySourceHeader = SdkHttpUtils.urlEncode(sourceBucketName + "/object/" + sourceObjectKey, false);
} else {
copySourceHeader = "/" + SdkHttpUtils.urlEncode(sourceBucketName, true)
+ "/" + SdkHttpUtils.urlEncode(sourceObjectKey, true);
}
if (sourceVersionId != null) {
copySourceHeader += "?versionId=" + sourceVersionId;
}
return copySourceHeader;
}
/**
*
* Populates the specified request with the numerous attributes available in
* SSEWithCustomerKeyRequest
.
*
ServerSideEncryptionWithCustomerKeyRequest
* object.
* @param sseKey
* The request object for an S3 operation that allows server-side
* encryption using customer-provided keys.
*/
private static void populateSSE_C(Request> request, SSECustomerKey sseKey) {
if (sseKey == null) return;
addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM,
sseKey.getAlgorithm());
addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY,
sseKey.getKey());
addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
sseKey.getMd5());
// Calculate the MD5 hash of the encryption key and fill it in the
// header, if the user didn't specify it in the metadata
if (sseKey.getKey() != null
&& sseKey.getMd5() == null) {
String encryptionKey_b64 = sseKey.getKey();
byte[] encryptionKey = Base64.decode(encryptionKey_b64);
request.addHeader(Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
Md5Utils.md5AsBase64(encryptionKey));
}
}
private static void populateSourceSSE_C(Request> request, SSECustomerKey sseKey) {
if (sseKey == null) return;
// Populate the SSE-C parameters for the source object
addHeaderIfNotNull(request, Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM,
sseKey.getAlgorithm());
addHeaderIfNotNull(request, Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY,
sseKey.getKey());
addHeaderIfNotNull(request, Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
sseKey.getMd5());
// Calculate the MD5 hash of the encryption key and fill it in the
// header, if the user didn't specify it in the metadata
if (sseKey.getKey() != null
&& sseKey.getMd5() == null) {
String encryptionKey_b64 = sseKey.getKey();
byte[] encryptionKey = Base64.decode(encryptionKey_b64);
request.addHeader(Headers.COPY_SOURCE_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
Md5Utils.md5AsBase64(encryptionKey));
}
}
private static void populateSSE_KMS(Request> request,
SSEAwsKeyManagementParams sseParams) {
if (sseParams != null) {
addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION,
sseParams.getEncryption());
addHeaderIfNotNull(request,
Headers.SERVER_SIDE_ENCRYPTION_AWS_KMS_KEYID,
sseParams.getAwsKmsKeyId());
addHeaderIfNotNull(request, Headers.SERVER_SIDE_ENCRYPTION_AWS_KMS_CONTEXT, sseParams.getAwsKmsEncryptionContext());
}
}
/**
* Adds the part number to the specified request, if partNumber is not null.
*
* @param request
* The request to add the partNumber to.
* @param partNumber
* The part number to be added.
*/
private void addPartNumberIfNotNull(Request> request, Integer partNumber) {
if (partNumber != null) {
request.addParameter("partNumber", partNumber.toString());
}
}
/**
* Adds the specified header to the specified request, if the header value
* is not null.
*
* @param request
* The request to add the header to.
* @param header
* The header name.
* @param value
* The header value.
*/
private static void addHeaderIfNotNull(Request> request, String header, String value) {
if (value != null) {
request.addHeader(header, value);
}
}
private static void addIntegerHeaderIfNotNull(Request> request, String header, Integer value) {
if (value != null) {
request.addHeader(header, Integer.toString(value));
}
}
/**
* Adds the specified parameter to the specified request, if the parameter
* value is not null.
*
* @param request
* The request to add the parameter to.
* @param paramName
* The parameter name.
* @param paramValue
* The parameter value.
*/
private static void addParameterIfNotNull(Request> request, String paramName, Integer paramValue) {
if (paramValue != null) {
addParameterIfNotNull(request, paramName, paramValue.toString());
}
}
/**
* Adds the specified parameter to the specified request, if the parameter
* value is not null.
*
* @param request
* The request to add the parameter to.
* @param paramName
* The parameter name.
* @param paramValue
* The parameter value.
*/
private static void addParameterIfNotNull(Request> request, String paramName, String paramValue) {
if (paramValue != null) {
request.addParameter(paramName, paramValue);
}
}
/**
*
* Adds the specified date header in RFC 822 date format to the specified
* request.
* This method will not add a date header if the specified date value is null
.
*
* Adds the specified string list header, joined together separated with
* commas, to the specified request.
* This method will not add a string list header if the specified values
* are null
or empty.
*
* Adds response headers parameters to the request given, if non-null. *
* * @param request * The request to add the response header parameters to. * @param responseHeaders * The full set of response headers to add, or null for none. */ private static void addResponseHeaderParameters(Request> request, ResponseHeaderOverrides responseHeaders) { if ( responseHeaders != null ) { if ( responseHeaders.getCacheControl() != null ) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CACHE_CONTROL, responseHeaders.getCacheControl()); } if ( responseHeaders.getContentDisposition() != null ) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_DISPOSITION, responseHeaders.getContentDisposition()); } if ( responseHeaders.getContentEncoding() != null ) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_ENCODING, responseHeaders.getContentEncoding()); } if ( responseHeaders.getContentLanguage() != null ) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_LANGUAGE, responseHeaders.getContentLanguage()); } if ( responseHeaders.getContentType() != null ) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_CONTENT_TYPE, responseHeaders.getContentType()); } if ( responseHeaders.getExpires() != null ) { request.addParameter(ResponseHeaderOverrides.RESPONSE_HEADER_EXPIRES, responseHeaders.getExpires()); } } } /** * Returns the URL to the key in the bucket given, using the client's scheme * and endpoint. Returns null if the given bucket and key cannot be * converted to a URL. */ public String getResourceUrl(String bucketName, String key) { try { return getUrl(bucketName, key).toString(); } catch ( Exception e ) { return null; } } @Override public URL getUrl(String bucketName, String key) { if (isArn(bucketName)) { throw new IllegalArgumentException("ARNs are not supported for getUrl in this SDK version. Please use S3Utilities " + "in the AWS SDK for Java 2.x."); } Request> request = new DefaultRequest