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.http.pipeline.stages;
17
18 import java.io.IOException;
19 import java.time.Duration;
20 import java.util.concurrent.TimeUnit;
21 import software.amazon.awssdk.annotations.SdkInternalApi;
22 import software.amazon.awssdk.core.Response;
23 import software.amazon.awssdk.core.exception.SdkException;
24 import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
25 import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
26 import software.amazon.awssdk.core.internal.http.pipeline.RequestPipeline;
27 import software.amazon.awssdk.core.internal.http.pipeline.RequestToResponsePipeline;
28 import software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper;
29 import software.amazon.awssdk.http.SdkHttpFullRequest;
30
31 /**
32  * Wrapper around the pipeline for a single request to provide retry, clock-skew and request throttling functionality.
33  */

34 @SdkInternalApi
35 public final class RetryableStage<OutputT> implements RequestToResponsePipeline<OutputT> {
36     private final RequestPipeline<SdkHttpFullRequest, Response<OutputT>> requestPipeline;
37     private final HttpClientDependencies dependencies;
38
39     public RetryableStage(HttpClientDependencies dependencies,
40                           RequestPipeline<SdkHttpFullRequest, Response<OutputT>> requestPipeline) {
41         this.dependencies = dependencies;
42         this.requestPipeline = requestPipeline;
43     }
44
45     public Response<OutputT> execute(SdkHttpFullRequest request, RequestExecutionContext context) throws Exception {
46         RetryableStageHelper retryableStageHelper = new RetryableStageHelper(request, context, dependencies);
47
48         while (true) {
49             retryableStageHelper.startingAttempt();
50
51             if (!retryableStageHelper.retryPolicyAllowsRetry()) {
52                 throw retryableStageHelper.retryPolicyDisallowedRetryException();
53             }
54
55             Duration backoffDelay = retryableStageHelper.getBackoffDelay();
56             if (!backoffDelay.isZero()) {
57                 retryableStageHelper.logBackingOff(backoffDelay);
58                 TimeUnit.MILLISECONDS.sleep(backoffDelay.toMillis());
59             }
60
61             Response<OutputT> response;
62             try {
63                 retryableStageHelper.logSendingRequest();
64                 response = requestPipeline.execute(retryableStageHelper.requestToSend(), context);
65             } catch (SdkException | IOException e) {
66                 retryableStageHelper.setLastException(e);
67                 continue;
68             }
69
70             retryableStageHelper.setLastResponse(response.httpResponse());
71
72             if (!response.isSuccess()) {
73                 retryableStageHelper.adjustClockIfClockSkew(response);
74                 retryableStageHelper.setLastException(response.exception());
75                 continue;
76             }
77
78             retryableStageHelper.attemptSucceeded();
79             return response;
80         }
81     }
82 }
83