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