• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.services.s3.internal.handlers;
17 
18 import static software.amazon.awssdk.utils.http.SdkHttpUtils.urlEncodeIgnoreSlashes;
19 
20 import software.amazon.awssdk.annotations.SdkInternalApi;
21 import software.amazon.awssdk.core.SdkRequest;
22 import software.amazon.awssdk.core.interceptor.Context.ModifyRequest;
23 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
24 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
25 import software.amazon.awssdk.services.s3.internal.resource.S3ArnUtils;
26 import software.amazon.awssdk.services.s3.internal.resource.S3ResourceType;
27 import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
28 import software.amazon.awssdk.services.s3.model.PutObjectRequest;
29 import software.amazon.awssdk.services.s3.model.UploadPartCopyRequest;
30 import software.amazon.awssdk.utils.Validate;
31 
32 /**
33  * This interceptor transforms the {@code sourceBucket}, {@code sourceKey}, and {@code sourceVersionId} parameters for
34  * {@link CopyObjectRequest} and {@link UploadPartCopyRequest} into a {@code copySource} parameter. The logic needed to
35  * construct a {@code copySource} can be considered non-trivial, so this interceptor facilitates allowing users to
36  * use higher-level constructs that more closely match other APIs, like {@link PutObjectRequest}. Additionally, this
37  * interceptor is responsible for URL encoding the relevant portions of the {@code copySource} value.
38  * <p>
39  * <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html#API_CopyObject_RequestParameters">API_CopyObject_RequestParameters</a>
40  * <p>
41  * <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html#API_UploadPartCopy_RequestParameters">API_UploadPartCopy_RequestParameters</a>
42  */
43 @SdkInternalApi
44 public final class CopySourceInterceptor implements ExecutionInterceptor {
45 
46     @Override
modifyRequest(ModifyRequest context, ExecutionAttributes executionAttributes)47     public SdkRequest modifyRequest(ModifyRequest context, ExecutionAttributes executionAttributes) {
48         SdkRequest request = context.request();
49         if (request instanceof CopyObjectRequest) {
50             return modifyCopyObjectRequest((CopyObjectRequest) request);
51         }
52         if (request instanceof UploadPartCopyRequest) {
53             return modifyUploadPartCopyRequest((UploadPartCopyRequest) request);
54         }
55         return request;
56     }
57 
modifyCopyObjectRequest(CopyObjectRequest request)58     private static SdkRequest modifyCopyObjectRequest(CopyObjectRequest request) {
59         if (request.copySource() != null) {
60             requireNotSet(request.sourceBucket(), "sourceBucket");
61             requireNotSet(request.sourceKey(), "sourceKey");
62             requireNotSet(request.sourceVersionId(), "sourceVersionId");
63             return request;
64         }
65         String copySource = constructCopySource(
66             requireSet(request.sourceBucket(), "sourceBucket"),
67             requireSet(request.sourceKey(), "sourceKey"),
68             request.sourceVersionId()
69         );
70         return request.toBuilder()
71                       .sourceBucket(null)
72                       .sourceKey(null)
73                       .sourceVersionId(null)
74                       .copySource(copySource)
75                       .build();
76     }
77 
modifyUploadPartCopyRequest(UploadPartCopyRequest request)78     private static SdkRequest modifyUploadPartCopyRequest(UploadPartCopyRequest request) {
79         if (request.copySource() != null) {
80             requireNotSet(request.sourceBucket(), "sourceBucket");
81             requireNotSet(request.sourceKey(), "sourceKey");
82             requireNotSet(request.sourceVersionId(), "sourceVersionId");
83             return request;
84         }
85         String copySource = constructCopySource(
86             requireSet(request.sourceBucket(), "sourceBucket"),
87             requireSet(request.sourceKey(), "sourceKey"),
88             request.sourceVersionId()
89         );
90         return request.toBuilder()
91                       .sourceBucket(null)
92                       .sourceKey(null)
93                       .sourceVersionId(null)
94                       .copySource(copySource)
95                       .build();
96     }
97 
constructCopySource(String sourceBucket, String sourceKey, String sourceVersionId)98     private static String constructCopySource(String sourceBucket, String sourceKey, String sourceVersionId) {
99         StringBuilder copySource = new StringBuilder();
100         copySource.append(urlEncodeIgnoreSlashes(sourceBucket));
101         S3ArnUtils.getArnType(sourceBucket).ifPresent(arnType -> {
102             if (arnType == S3ResourceType.ACCESS_POINT || arnType == S3ResourceType.OUTPOST) {
103                 copySource.append("/object");
104             }
105         });
106         copySource.append("/");
107         copySource.append(urlEncodeIgnoreSlashes(sourceKey));
108         if (sourceVersionId != null) {
109             copySource.append("?versionId=");
110             copySource.append(urlEncodeIgnoreSlashes(sourceVersionId));
111         }
112         return copySource.toString();
113     }
114 
requireNotSet(Object value, String paramName)115     private static void requireNotSet(Object value, String paramName) {
116         Validate.isTrue(value == null, "Parameter 'copySource' must not be used in conjunction with '%s'",
117                         paramName);
118     }
119 
requireSet(T value, String paramName)120     private static <T> T requireSet(T value, String paramName) {
121         Validate.isTrue(value != null, "Parameter '%s' must not be null",
122                         paramName);
123         return value;
124     }
125 }
126