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;
17
18 import java.time.Duration;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Objects;
25 import java.util.Optional;
26 import java.util.TreeMap;
27 import java.util.function.Consumer;
28 import software.amazon.awssdk.annotations.Immutable;
29 import software.amazon.awssdk.annotations.SdkPublicApi;
30 import software.amazon.awssdk.core.signer.Signer;
31 import software.amazon.awssdk.utils.CollectionUtils;
32 import software.amazon.awssdk.utils.Validate;
33
34 /**
35  * Base per-request override configuration for all SDK requests.
36  */

37 @Immutable
38 @SdkPublicApi
39 public abstract class RequestOverrideConfiguration {
40
41     private final Map<String, List<String>> headers;
42     private final Map<String, List<String>> rawQueryParameters;
43     private final List<ApiName> apiNames;
44     private final Duration apiCallTimeout;
45     private final Duration apiCallAttemptTimeout;
46     private final Signer signer;
47
48     protected RequestOverrideConfiguration(Builder<?> builder) {
49         this.headers = CollectionUtils.deepUnmodifiableMap(builder.headers(), () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
50         this.rawQueryParameters = CollectionUtils.deepUnmodifiableMap(builder.rawQueryParameters());
51         this.apiNames = Collections.unmodifiableList(new ArrayList<>(builder.apiNames()));
52         this.apiCallTimeout = Validate.isPositiveOrNull(builder.apiCallTimeout(), "apiCallTimeout");
53         this.apiCallAttemptTimeout = Validate.isPositiveOrNull(builder.apiCallAttemptTimeout(), "apiCallAttemptTimeout");
54         this.signer = builder.signer();
55     }
56
57     /**
58      * Optional additional headers to be added to the HTTP request.
59      *
60      * @return The optional additional headers.
61      */

62     public Map<String, List<String>> headers() {
63         return headers;
64     }
65
66     /**
67      * Optional additional query parameters to be added to the HTTP request.
68      *
69      * @return The optional additional query parameters.
70      */

71     public Map<String, List<String>> rawQueryParameters() {
72         return rawQueryParameters;
73     }
74
75     /**
76      * The optional names of the higher level libraries that constructed the request.
77      *
78      * @return The names of the libraries.
79      */

80     public List<ApiName> apiNames() {
81         return apiNames;
82     }
83
84     /**
85      * The amount of time to allow the client to complete the execution of an API call. This timeout covers the entire client
86      * execution except for marshalling. This includes request handler execution, all HTTP requests including retries,
87      * unmarshalling, etc. This value should always be positive, if present.
88      *
89      * <p>The api call timeout feature doesn't have strict guarantees on how quickly a request is aborted when the
90      * timeout is breached. The typical case aborts the request within a few milliseconds but there may occasionally be
91      * requests that don't get aborted until several seconds after the timer has been breached. Because of this, the client
92      * execution timeout feature should not be used when absolute precision is needed.
93      *
94      * <p>This may be used together with {@link #apiCallAttemptTimeout()} to enforce both a timeout on each individual HTTP
95      * request (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
96      *
97      * @see Builder#apiCallTimeout(Duration)
98      */

99     public Optional<Duration> apiCallTimeout() {
100         return Optional.ofNullable(apiCallTimeout);
101     }
102
103     /**
104      * The amount of time to wait for the http request to complete before giving up and timing out. This value should always be
105      * positive, if present.
106      *
107      * <p>The request timeout feature doesn't have strict guarantees on how quickly a request is aborted when the timeout is
108      * breached. The typical case aborts the request within a few milliseconds but there may occasionally be requests that
109      * don't get aborted until several seconds after the timer has been breached. Because of this, the request timeout
110      * feature should not be used when absolute precision is needed.
111      *
112      * <p>This may be used together with {@link #apiCallTimeout()} to enforce both a timeout on each individual HTTP
113      * request
114      * (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
115      *
116      * @see Builder#apiCallAttemptTimeout(Duration)
117      */

118     public Optional<Duration> apiCallAttemptTimeout() {
119         return Optional.ofNullable(apiCallAttemptTimeout);
120     }
121
122     /**
123      * @return the signer for signing the request. This signer get priority over the signer set on the client while
124      * signing the requests. If this value is not set, then the client level signer is used for signing the request.
125      */

126     public Optional<Signer> signer() {
127         return Optional.ofNullable(signer);
128     }
129
130     @Override
131     public boolean equals(Object o) {
132         if (this == o) {
133             return true;
134         }
135         if (o == null || getClass() != o.getClass()) {
136             return false;
137         }
138         RequestOverrideConfiguration that = (RequestOverrideConfiguration) o;
139         return Objects.equals(headers, that.headers) &&
140                Objects.equals(rawQueryParameters, that.rawQueryParameters) &&
141                Objects.equals(apiNames, that.apiNames) &&
142                Objects.equals(apiCallTimeout, that.apiCallTimeout) &&
143                Objects.equals(apiCallAttemptTimeout, that.apiCallAttemptTimeout) &&
144                Objects.equals(signer, that.signer);
145     }
146
147     @Override
148     public int hashCode() {
149         int hashCode = 1;
150         hashCode = 31 * hashCode + Objects.hashCode(headers);
151         hashCode = 31 * hashCode + Objects.hashCode(rawQueryParameters);
152         hashCode = 31 * hashCode + Objects.hashCode(apiNames);
153         hashCode = 31 * hashCode + Objects.hashCode(apiCallTimeout);
154         hashCode = 31 * hashCode + Objects.hashCode(apiCallAttemptTimeout);
155         hashCode = 31 * hashCode + Objects.hashCode(signer);
156         return hashCode;
157     }
158
159     /**
160      * Create a {@link Builder} initialized with the properties of this {@code SdkRequestOverrideConfiguration}.
161      *
162      * @return A new builder intialized with this config's properties.
163      */

164     public abstract Builder<? extends Builder> toBuilder();
165
166     public interface Builder<B extends Builder> {
167         /**
168          * Optional additional headers to be added to the HTTP request.
169          *
170          * @return The optional additional headers.
171          */

172         Map<String, List<String>> headers();
173
174         /**
175          * Add a single header to be set on the HTTP request.
176          * <p>
177          * This overrides any values for the given header set on the request by default by the SDK, as well as header
178          * overrides set at the client level using
179          * {@link software.amazon.awssdk.core.client.config.ClientOverrideConfiguration}.
180          *
181          * <p>
182          * This overrides any values already configured with this header name in the builder.
183          *
184          * @param name The name of the header.
185          * @param value The value of the header.
186          * @return This object for method chaining.
187          */

188         default B putHeader(String name, String value) {
189             putHeader(name, Collections.singletonList(value));
190             return (B) this;
191         }
192
193         /**
194          * Add a single header with multiple values to be set on the HTTP request.
195          * <p>
196          * This overrides any values for the given header set on the request by default by the SDK, as well as header
197          * overrides set at the client level using
198          * {@link software.amazon.awssdk.core.client.config.ClientOverrideConfiguration}.
199          *
200          * <p>
201          * This overrides any values already configured with this header name in the builder.
202          *
203          * @param name The name of the header.
204          * @param values The values of the header.
205          * @return This object for method chaining.
206          */

207         B putHeader(String name, List<String> values);
208
209         /**
210          * Add additional headers to be set on the HTTP request.
211          * <p>
212          * This overrides any values for the given headers set on the request by default by the SDK, as well as header
213          * overrides set at the client level using
214          * {@link software.amazon.awssdk.core.client.config.ClientOverrideConfiguration}.
215          *
216          * <p>
217          * This completely overrides any values currently configured in the builder.
218          *
219          * @param headers The set of additional headers.
220          * @return This object for method chaining.
221          */

222         B headers(Map<String, List<String>> headers);
223
224         /**
225          * Optional additional query parameters to be added to the HTTP request.
226          *
227          * @return The optional additional query parameters.
228          */

229         Map<String, List<String>> rawQueryParameters();
230
231         /**
232          * Add a single query parameter to be set on the HTTP request.
233          *
234          * <p>
235          * This overrides any values already configured with this query name in the builder.
236          *
237          * @param name The query parameter name.
238          * @param value The query parameter value.
239          * @return This object for method chaining.
240          */

241         default B putRawQueryParameter(String name, String value) {
242             putRawQueryParameter(name, Collections.singletonList(value));
243             return (B) this;
244         }
245
246         /**
247          * Add a single query parameter with multiple values to be set on the HTTP request.
248          *
249          * <p>
250          * This overrides any values already configured with this query name in the builder.
251          *
252          * @param name The query parameter name.
253          * @param values The query parameter values.
254          * @return This object for method chaining.
255          */

256         B putRawQueryParameter(String name, List<String> values);
257
258         /**
259          * Configure query parameters to be set on the HTTP request.
260          *
261          * <p>
262          * This completely overrides any query parameters currently configured in the builder.
263          *
264          * @param rawQueryParameters The set of additional query parameters.
265          * @return This object for method chaining.
266          */

267         B rawQueryParameters(Map<String, List<String>> rawQueryParameters);
268
269         /**
270          * The optional names of the higher level libraries that constructed the request.
271          *
272          * @return The names of the libraries.
273          */

274         List<ApiName> apiNames();
275
276         /**
277          * Set the optional name of the higher level library that constructed the request.
278          *
279          * @param apiName The name of the library.
280          *
281          * @return This object for method chaining.
282          */

283         B addApiName(ApiName apiName);
284
285         /**
286          * Set the optional name of the higher level library that constructed the request.
287          *
288          * @param apiNameConsumer A {@link Consumer} that accepts a {@link ApiName.Builder}.
289          *
290          * @return This object for method chaining.
291          */

292         B addApiName(Consumer<ApiName.Builder> apiNameConsumer);
293
294         /**
295          * Configure the amount of time to allow the client to complete the execution of an API call. This timeout covers the
296          * entire client execution except for marshalling. This includes request handler execution, all HTTP requests including
297          * retries, unmarshalling, etc. This value should always be positive, if present.
298          *
299          * <p>The api call timeout feature doesn't have strict guarantees on how quickly a request is aborted when the
300          * timeout is breached. The typical case aborts the request within a few milliseconds but there may occasionally be
301          * requests that don't get aborted until several seconds after the timer has been breached. Because of this, the client
302          * execution timeout feature should not be used when absolute precision is needed.
303          *
304          * <p>This may be used together with {@link #apiCallAttemptTimeout()} to enforce both a timeout on each individual HTTP
305          * request (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
306          *
307          * @see RequestOverrideConfiguration#apiCallTimeout()
308          */

309         B apiCallTimeout(Duration apiCallTimeout);
310
311         Duration apiCallTimeout();
312
313         /**
314          * Configure the amount of time to wait for the http request to complete before giving up and timing out. This value
315          * should always be positive, if present.
316          *
317          * <p>The request timeout feature doesn't have strict guarantees on how quickly a request is aborted when the timeout is
318          * breached. The typical case aborts the request within a few milliseconds but there may occasionally be requests that
319          * don't get aborted until several seconds after the timer has been breached. Because of this, the request timeout
320          * feature should not be used when absolute precision is needed.
321          *
322          * <p>This may be used together with {@link #apiCallTimeout()} to enforce both a timeout on each individual HTTP
323          * request (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
324          *
325          * @see RequestOverrideConfiguration#apiCallAttemptTimeout()
326          */

327         B apiCallAttemptTimeout(Duration apiCallAttemptTimeout);
328
329         Duration apiCallAttemptTimeout();
330
331         /**
332          * Sets the signer to use for signing the request. This signer get priority over the signer set on the client while
333          * signing the requests. If this value is null, then the client level signer is used for signing the request.
334          *
335          * @param signer Signer for signing the request
336          * @return This object for method chaining
337          */

338         B signer(Signer signer);
339
340         Signer signer();
341
342         /**
343          * Create a new {@code SdkRequestOverrideConfiguration} with the properties set on this builder.
344          *
345          * @return The new {@code SdkRequestOverrideConfiguration}.
346          */

347         RequestOverrideConfiguration build();
348     }
349
350     protected abstract static class BuilderImpl<B extends Builder> implements Builder<B> {
351         private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
352         private Map<String, List<String>> rawQueryParameters = new HashMap<>();
353         private List<ApiName> apiNames = new ArrayList<>();
354         private Duration apiCallTimeout;
355         private Duration apiCallAttemptTimeout;
356         private Signer signer;
357
358         protected BuilderImpl() {
359         }
360
361         protected BuilderImpl(RequestOverrideConfiguration sdkRequestOverrideConfig) {
362             headers(sdkRequestOverrideConfig.headers);
363             rawQueryParameters(sdkRequestOverrideConfig.rawQueryParameters);
364             sdkRequestOverrideConfig.apiNames.forEach(this::addApiName);
365         }
366
367         @Override
368         public Map<String, List<String>> headers() {
369             return CollectionUtils.unmodifiableMapOfLists(headers);
370         }
371
372         @Override
373         public B putHeader(String name, List<String> values) {
374             Validate.paramNotNull(values, "values");
375             headers.put(name, new ArrayList<>(values));
376             return (B) this;
377         }
378
379         @Override
380         @SuppressWarnings("unchecked")
381         public B headers(Map<String, List<String>> headers) {
382             Validate.paramNotNull(headers, "headers");
383             this.headers = CollectionUtils.deepCopyMap(headers);
384             return (B) this;
385         }
386
387         @Override
388         public Map<String, List<String>> rawQueryParameters() {
389             return CollectionUtils.unmodifiableMapOfLists(rawQueryParameters);
390         }
391
392         @Override
393         public B putRawQueryParameter(String name, List<String> values) {
394             Validate.paramNotNull(name, "name");
395             Validate.paramNotNull(values, "values");
396             rawQueryParameters.put(name, new ArrayList<>(values));
397             return (B) this;
398         }
399
400         @Override
401         @SuppressWarnings("unchecked")
402         public B rawQueryParameters(Map<String, List<String>> rawQueryParameters) {
403             Validate.paramNotNull(rawQueryParameters, "rawQueryParameters");
404             this.rawQueryParameters = CollectionUtils.deepCopyMap(rawQueryParameters);
405             return (B) this;
406         }
407
408         @Override
409         public List<ApiName> apiNames() {
410             return Collections.unmodifiableList(apiNames);
411         }
412
413         @Override
414         @SuppressWarnings("unchecked")
415         public B addApiName(ApiName apiName) {
416             this.apiNames.add(apiName);
417             return (B) this;
418         }
419
420         @Override
421         @SuppressWarnings("unchecked")
422         public B addApiName(Consumer<ApiName.Builder> apiNameConsumer) {
423             ApiName.Builder b = ApiName.builder();
424             apiNameConsumer.accept(b);
425             addApiName(b.build());
426             return (B) this;
427         }
428
429         @Override
430         public B apiCallTimeout(Duration apiCallTimeout) {
431             this.apiCallTimeout = apiCallTimeout;
432             return (B) this;
433         }
434
435         public void setApiCallTimeout(Duration apiCallTimeout) {
436             apiCallTimeout(apiCallTimeout);
437         }
438
439         @Override
440         public Duration apiCallTimeout() {
441             return apiCallTimeout;
442         }
443
444         @Override
445         public B apiCallAttemptTimeout(Duration apiCallAttemptTimeout) {
446             this.apiCallAttemptTimeout = apiCallAttemptTimeout;
447             return (B) this;
448         }
449
450         public void setApiCallAttemptTimeout(Duration apiCallAttemptTimeout) {
451             apiCallAttemptTimeout(apiCallAttemptTimeout);
452         }
453
454         @Override
455         public Duration apiCallAttemptTimeout() {
456             return apiCallAttemptTimeout;
457         }
458
459         @Override
460         public B signer(Signer signer) {
461             this.signer = signer;
462             return (B) this;
463         }
464
465         public void setSigner(Signer signer) {
466             signer(signer);
467         }
468
469         @Override
470         public Signer signer() {
471             return signer;
472         }
473     }
474 }
475