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.core.internal.handler;
17
18 import java.net.URI;
19 import java.util.function.BiFunction;
20 import software.amazon.awssdk.annotations.SdkInternalApi;
21 import software.amazon.awssdk.core.Response;
22 import software.amazon.awssdk.core.SdkRequest;
23 import software.amazon.awssdk.core.SdkResponse;
24 import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
25 import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
26 import software.amazon.awssdk.core.client.config.SdkClientOption;
27 import software.amazon.awssdk.core.client.handler.ClientExecutionParams;
28 import software.amazon.awssdk.core.http.ExecutionContext;
29 import software.amazon.awssdk.core.http.HttpResponseHandler;
30 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
31 import software.amazon.awssdk.core.interceptor.ExecutionInterceptorChain;
32 import software.amazon.awssdk.core.interceptor.InterceptorContext;
33 import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
34 import software.amazon.awssdk.core.internal.InternalCoreExecutionAttribute;
35 import software.amazon.awssdk.http.SdkHttpFullRequest;
36 import software.amazon.awssdk.http.SdkHttpFullResponse;
37 import software.amazon.awssdk.utils.StringUtils;
38
39 @SdkInternalApi
40 public abstract class BaseClientHandler {
41     private SdkClientConfiguration clientConfiguration;
42
43     protected BaseClientHandler(SdkClientConfiguration clientConfiguration) {
44         this.clientConfiguration = clientConfiguration;
45     }
46
47     /**
48      * Finalize {@link SdkRequest} by running beforeExecution and modifyRequest interceptors.
49      *
50      * @param executionContext the execution context
51      * @return the {@link InterceptorContext}
52      */

53     static InterceptorContext finalizeSdkRequest(ExecutionContext executionContext) {
54         runBeforeExecutionInterceptors(executionContext);
55         return runModifyRequestInterceptors(executionContext);
56     }
57
58     /**
59      * Finalize {@link SdkHttpFullRequest} by running beforeMarshalling, afterMarshalling,
60      * modifyHttpRequest, modifyHttpContent and modifyAsyncHttpContent interceptors
61      */

62     static <InputT extends SdkRequest, OutputT> InterceptorContext finalizeSdkHttpFullRequest(
63         ClientExecutionParams<InputT, OutputT> executionParams,
64         ExecutionContext executionContext, InputT inputT,
65         SdkClientConfiguration clientConfiguration) {
66
67         runBeforeMarshallingInterceptors(executionContext);
68         SdkHttpFullRequest request = executionParams.getMarshaller().marshall(inputT);
69         request = modifyEndpointHostIfNeeded(request, clientConfiguration, executionParams);
70
71         addHttpRequest(executionContext, request);
72         runAfterMarshallingInterceptors(executionContext);
73         return runModifyHttpRequestAndHttpContentInterceptors(executionContext);
74     }
75
76     private static void runBeforeExecutionInterceptors(ExecutionContext executionContext) {
77         executionContext.interceptorChain().beforeExecution(executionContext.interceptorContext(),
78                                                             executionContext.executionAttributes());
79     }
80
81     private static InterceptorContext runModifyRequestInterceptors(ExecutionContext executionContext) {
82         InterceptorContext interceptorContext =
83             executionContext.interceptorChain().modifyRequest(executionContext.interceptorContext(),
84                                                               executionContext.executionAttributes());
85         executionContext.interceptorContext(interceptorContext);
86         return interceptorContext;
87     }
88
89     private static void runBeforeMarshallingInterceptors(ExecutionContext executionContext) {
90         executionContext.interceptorChain().beforeMarshalling(executionContext.interceptorContext(),
91                                                               executionContext.executionAttributes());
92     }
93
94     /**
95      * Modifies the given {@link SdkHttpFullRequest} with new host if host prefix is enabled and set.
96      */

97     private static SdkHttpFullRequest modifyEndpointHostIfNeeded(SdkHttpFullRequest originalRequest,
98                                                                  SdkClientConfiguration clientConfiguration,
99                                                                  ClientExecutionParams executionParams) {
100         if (executionParams.discoveredEndpoint() != null) {
101             URI discoveredEndpoint = executionParams.discoveredEndpoint();
102             return originalRequest.toBuilder().host(discoveredEndpoint.getHost()).port(discoveredEndpoint.getPort()).build();
103         }
104
105         Boolean disableHostPrefixInjection = clientConfiguration.option(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION);
106         if ((disableHostPrefixInjection != null && disableHostPrefixInjection.equals(Boolean.TRUE)) ||
107             StringUtils.isEmpty(executionParams.hostPrefixExpression())) {
108             return originalRequest;
109         }
110
111         return originalRequest.toBuilder()
112                               .host(executionParams.hostPrefixExpression() + originalRequest.host())
113                               .build();
114     }
115
116     private static void addHttpRequest(ExecutionContext executionContext, SdkHttpFullRequest request) {
117         InterceptorContext interceptorContext = executionContext.interceptorContext().copy(b -> b.httpRequest(request));
118         executionContext.interceptorContext(interceptorContext);
119     }
120
121     private static void runAfterMarshallingInterceptors(ExecutionContext executionContext) {
122         executionContext.interceptorChain().afterMarshalling(executionContext.interceptorContext(),
123                                                              executionContext.executionAttributes());
124     }
125
126     private static InterceptorContext runModifyHttpRequestAndHttpContentInterceptors(ExecutionContext executionContext) {
127         InterceptorContext interceptorContext =
128             executionContext.interceptorChain().modifyHttpRequestAndHttpContent(executionContext.interceptorContext(),
129                                                                                 executionContext.executionAttributes());
130         executionContext.interceptorContext(interceptorContext);
131         return interceptorContext;
132     }
133
134     /**
135      * Run afterUnmarshalling and modifyResponse interceptors.
136      */

137     private static <OutputT extends SdkResponse> BiFunction<OutputT, SdkHttpFullResponse, OutputT>
138         runAfterUnmarshallingInterceptors(ExecutionContext context) {
139
140         return (input, httpFullResponse) -> {
141             // Update interceptor context to include response
142             InterceptorContext interceptorContext =
143                 context.interceptorContext().copy(b -> b.response(input));
144
145             context.interceptorChain().afterUnmarshalling(interceptorContext, context.executionAttributes());
146
147             interceptorContext = context.interceptorChain().modifyResponse(interceptorContext, context.executionAttributes());
148
149             // Store updated context
150             context.interceptorContext(interceptorContext);
151
152             return (OutputT) interceptorContext.response();
153         };
154     }
155
156     private static <OutputT extends SdkResponse> BiFunction<OutputT, SdkHttpFullResponse, OutputT>
157         attachHttpResponseToResult() {
158
159         return ((response, httpFullResponse) ->
160                     (OutputT) response.toBuilder().sdkHttpResponse(httpFullResponse).build());
161     }
162
163     static ExecutionAttributes createInitialExecutionAttributes() {
164         return new ExecutionAttributes().putAttribute(InternalCoreExecutionAttribute.EXECUTION_ATTEMPT, 1);
165     }
166
167     protected <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext createExecutionContext(
168         ClientExecutionParams<InputT, OutputT> params, ExecutionAttributes executionAttributes) {
169
170         SdkRequest originalRequest = params.getInput();
171
172         executionAttributes
173             .putAttribute(SdkExecutionAttribute.SERVICE_CONFIG,
174                           clientConfiguration.option(SdkClientOption.SERVICE_CONFIGURATION))
175             .putAttribute(SdkExecutionAttribute.SERVICE_NAME, clientConfiguration.option(SdkClientOption.SERVICE_NAME));
176
177         ExecutionInterceptorChain interceptorChain =
178                 new ExecutionInterceptorChain(clientConfiguration.option(SdkClientOption.EXECUTION_INTERCEPTORS));
179
180         return ExecutionContext.builder()
181                                .interceptorChain(interceptorChain)
182                                .interceptorContext(InterceptorContext.builder()
183                                                                      .request(originalRequest)
184                                                                      .build())
185                                .executionAttributes(executionAttributes)
186                                .signer(clientConfiguration.option(SdkAdvancedClientOption.SIGNER))
187                                .build();
188     }
189
190     protected boolean isCalculateCrc32FromCompressedData() {
191         return clientConfiguration.option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED);
192     }
193
194     /**
195      * Decorate response handlers by running after unmarshalling Interceptors and adding http response metadata.
196      */

197     <OutputT extends SdkResponse> HttpResponseHandler<OutputT> decorateResponseHandlers(
198         HttpResponseHandler<OutputT> delegate, ExecutionContext executionContext) {
199
200         return resultTransformationResponseHandler(delegate, responseTransformations(executionContext));
201     }
202
203     <OutputT extends SdkResponse> HttpResponseHandler<Response<OutputT>> decorateSuccessResponseHandlers(
204         HttpResponseHandler<Response<OutputT>> delegate, ExecutionContext executionContext) {
205
206         return successTransformationResponseHandler(delegate, responseTransformations(executionContext));
207     }
208
209     <OutputT extends SdkResponse> HttpResponseHandler<Response<OutputT>> successTransformationResponseHandler(
210         HttpResponseHandler<Response<OutputT>> responseHandler,
211         BiFunction<OutputT, SdkHttpFullResponse, OutputT> successTransformer) {
212
213         return (response, executionAttributes) -> {
214             Response<OutputT> delegateResponse = responseHandler.handle(response, executionAttributes);
215
216             if (delegateResponse.isSuccess()) {
217                 return delegateResponse.toBuilder()
218                                        .response(successTransformer.apply(delegateResponse.response(), response))
219                                        .build();
220             } else {
221                 return delegateResponse;
222             }
223         };
224     }
225
226     <OutputT extends SdkResponse> HttpResponseHandler<OutputT> resultTransformationResponseHandler(
227         HttpResponseHandler<OutputT> responseHandler,
228         BiFunction<OutputT, SdkHttpFullResponse, OutputT> successTransformer) {
229
230         return (response, executionAttributes) -> {
231             OutputT delegateResponse = responseHandler.handle(response, executionAttributes);
232             return successTransformer.apply(delegateResponse, response);
233         };
234     }
235
236     static void validateExecutionParams(ClientExecutionParams<?, ?> executionParams) {
237         if (executionParams.getCombinedResponseHandler() != null) {
238             if (executionParams.getResponseHandler() != null) {
239                 throw new IllegalArgumentException("Only one of 'combinedResponseHandler' and 'responseHandler' may "
240                                                    + "be specified in a ClientExecutionParams object");
241             }
242
243             if (executionParams.getErrorResponseHandler() != null) {
244                 throw new IllegalArgumentException("Only one of 'combinedResponseHandler' and 'errorResponseHandler' "
245                                                    + "may be specified in a ClientExecutionParams object");
246             }
247         }
248     }
249
250     /**
251      * Returns the composition of 'runAfterUnmarshallingInterceptors' and 'attachHttpResponseToResult' response
252      * transformations as a single transformation that should be applied to all responses.
253      */

254     private static <T extends SdkResponse> BiFunction<T, SdkHttpFullResponse, T>
255         responseTransformations(ExecutionContext executionContext) {
256
257         return composeResponseFunctions(runAfterUnmarshallingInterceptors(executionContext),
258                                         attachHttpResponseToResult());
259     }
260
261     /**
262      * Composes two functions passing the result of the first function as the first argument of the second function
263      * and the same second argument to both functions. This is used by response transformers to chain together and
264      * pass through a persistent SdkHttpFullResponse object as a second arg turning them effectively into a single
265      * response transformer.
266      * <p>
267      * So given f1(x, y) and f2(x, y) where x is typically OutputT and y is typically SdkHttpFullResponse the composed
268      * function would be f12(x, y) = f2(f1(x, y), y).
269      */

270     private static <T, R> BiFunction<T, R, T> composeResponseFunctions(BiFunction<T, R, T> function1,
271                                                                        BiFunction<T, R, T> function2) {
272         return (x, y) -> function2.apply(function1.apply(x, y), y);
273     }
274 }
275