1
15
16 package com.amazonaws.internal;
17
18 import com.amazonaws.ClientConfiguration;
19 import com.amazonaws.Request;
20 import com.amazonaws.annotation.SdkInternalApi;
21 import com.amazonaws.annotation.ThreadSafe;
22 import com.amazonaws.auth.SdkClock;
23 import com.amazonaws.auth.internal.AWS4SignerUtils;
24 import com.amazonaws.handlers.HandlerContextKey;
25 import com.amazonaws.retry.ClockSkewAdjuster;
26 import com.amazonaws.retry.RetryPolicyAdapter;
27 import com.amazonaws.retry.v2.RetryPolicy;
28 import java.util.ArrayList;
29 import java.util.List;
30
31
34 @ThreadSafe
35 @SdkInternalApi
36 public final class SdkRequestRetryHeaderProvider {
37 private static final String SDK_REQUEST_RETRY_HEADER = "amz-sdk-request";
38 private final ClientConfiguration config;
39 private final Integer maxErrorRetry;
40 private final ClockSkewAdjuster clockSkewAdjuster;
41
42 public SdkRequestRetryHeaderProvider(ClientConfiguration config,
43 RetryPolicy retryPolicy,
44 ClockSkewAdjuster clockSkewAdjuster) {
45 this.config = config;
46 if (retryPolicy instanceof RetryPolicyAdapter) {
47 maxErrorRetry = ((RetryPolicyAdapter) retryPolicy).getMaxErrorRetry() + 1;
48 } else {
49 maxErrorRetry = null;
50 }
51 this.clockSkewAdjuster = clockSkewAdjuster;
52 }
53
54 public void addSdkRequestRetryHeader(Request<?> request, int attemptNum) {
55 List<Pair> pairs = requestPairs(request, String.valueOf(attemptNum));
56
57 StringBuilder headerValue = new StringBuilder();
58 for (Pair pair: pairs) {
59 headerValue.append(pair.name).append("=").append(pair.value).append(";");
60 }
61
62 String header = headerValue.toString().substring(0, headerValue.length() - 1);
63 request.addHeader(SDK_REQUEST_RETRY_HEADER, header);
64 }
65
66 private List<Pair> requestPairs(Request<?> request, String attemptNum) {
67 List<Pair> requestPairs = new ArrayList<Pair>();
68
69 String optionalTtl = calculateTtl(request);
70 if (optionalTtl != null) {
71 requestPairs.add(new Pair("ttl", optionalTtl));
72 }
73
74 requestPairs.add(new Pair("attempt", attemptNum));
75
76 if (maxErrorRetry != null) {
77 requestPairs.add(new Pair("max", String.valueOf(maxErrorRetry)));
78 }
79
80 return requestPairs;
81 }
82
83
89 private String calculateTtl(Request<?> request) {
90
91 if (isStreaming(request)) {
92 return null;
93 }
94
95 Integer estimatedSkew = clockSkewAdjuster.getEstimatedSkew();
96
97
98 if (estimatedSkew == null) {
99 return null;
100 }
101
102 long currentTimeMillis = SdkClock.Instance.get().currentTimeMillis();
103
104
105 long ttl = currentTimeMillis + config.getSocketTimeout() - estimatedSkew * 1000;
106
107 return AWS4SignerUtils.formatTimestamp(ttl);
108 }
109
110 private boolean isStreaming(Request<?> request) {
111 return Boolean.TRUE.equals(request.getHandlerContext(HandlerContextKey.HAS_STREAMING_INPUT)) ||
112 Boolean.TRUE.equals(request.getHandlerContext(HandlerContextKey.HAS_STREAMING_OUTPUT));
113 }
114
115 private static final class Pair {
116 private String name;
117 private String value;
118
119 Pair(String name, String value) {
120 this.name = name;
121 this.value = value;
122 }
123 }
124 }
125