1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.impl.classic;
28
29 import java.io.IOException;
30 import java.io.InterruptedIOException;
31
32 import org.apache.hc.client5.http.HttpRequestRetryStrategy;
33 import org.apache.hc.client5.http.HttpRoute;
34 import org.apache.hc.client5.http.classic.ExecChain;
35 import org.apache.hc.client5.http.classic.ExecChain.Scope;
36 import org.apache.hc.client5.http.classic.ExecChainHandler;
37 import org.apache.hc.client5.http.impl.ChainElement;
38 import org.apache.hc.client5.http.protocol.HttpClientContext;
39 import org.apache.hc.core5.annotation.Contract;
40 import org.apache.hc.core5.annotation.Internal;
41 import org.apache.hc.core5.annotation.ThreadingBehavior;
42 import org.apache.hc.core5.http.ClassicHttpRequest;
43 import org.apache.hc.core5.http.ClassicHttpResponse;
44 import org.apache.hc.core5.http.HttpEntity;
45 import org.apache.hc.core5.http.HttpException;
46 import org.apache.hc.core5.http.HttpHost;
47 import org.apache.hc.core5.http.NoHttpResponseException;
48 import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
49 import org.apache.hc.core5.util.Args;
50 import org.apache.hc.core5.util.TimeValue;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 @Contract(threading = ThreadingBehavior.STATELESS)
84 @Internal
85 public class HttpRequestRetryExec implements ExecChainHandler {
86
87 private static final Logger LOG = LoggerFactory.getLogger(HttpRequestRetryExec.class);
88
89 private final HttpRequestRetryStrategy retryStrategy;
90
91 public HttpRequestRetryExec(
92 final HttpRequestRetryStrategy retryStrategy) {
93 Args.notNull(retryStrategy, "retryStrategy");
94 this.retryStrategy = retryStrategy;
95 }
96
97 @Override
98 public ClassicHttpResponse execute(
99 final ClassicHttpRequest request,
100 final Scope scope,
101 final ExecChain chain) throws IOException, HttpException {
102 Args.notNull(request, "request");
103 Args.notNull(scope, "scope");
104 final String exchangeId = scope.exchangeId;
105 final HttpRoute route = scope.route;
106 final HttpHost target = route.getTargetHost();
107 final HttpClientContext context = scope.clientContext;
108 ClassicHttpRequest currentRequest = request;
109
110 for (int execCount = 1;; execCount++) {
111 try {
112 final ClassicHttpResponse response = chain.proceed(currentRequest, scope);
113 try {
114 final HttpEntity entity = request.getEntity();
115 if (entity != null && !entity.isRepeatable()) {
116 if (LOG.isDebugEnabled()) {
117 LOG.debug("{} cannot retry non-repeatable request", exchangeId);
118 }
119 return response;
120 }
121 if (retryStrategy.retryRequest(response, execCount, context)) {
122 response.close();
123 final TimeValue delay = retryStrategy.getRetryInterval(response, execCount, context);
124 if (LOG.isInfoEnabled()) {
125 LOG.info("{} {} responded with status {}; " +
126 "request will be automatically re-executed in {} (exec count {})",
127 exchangeId, target, response.getCode(), delay, execCount + 1);
128 }
129 pause(delay);
130 currentRequest = ClassicRequestBuilder.copy(scope.originalRequest).build();
131 } else {
132 return response;
133 }
134 } catch (final RuntimeException ex) {
135 response.close();
136 throw ex;
137 }
138 } catch (final IOException ex) {
139 if (scope.execRuntime.isExecutionAborted()) {
140 throw new RequestFailedException("Request aborted");
141 }
142 final HttpEntity requestEntity = request.getEntity();
143 if (requestEntity != null && !requestEntity.isRepeatable()) {
144 if (LOG.isDebugEnabled()) {
145 LOG.debug("{} cannot retry non-repeatable request", exchangeId);
146 }
147 throw ex;
148 }
149 if (retryStrategy.retryRequest(request, ex, execCount, context)) {
150 if (LOG.isDebugEnabled()) {
151 LOG.debug("{} {}", exchangeId, ex.getMessage(), ex);
152 }
153 final TimeValue delay = retryStrategy.getRetryInterval(request, ex, execCount, context);
154 if (LOG.isInfoEnabled()) {
155 LOG.info("{} recoverable I/O exception ({}) caught when sending request to {};" +
156 "request will be automatically re-executed in {} (exec count {})",
157 exchangeId, ex.getClass().getName(), target, delay, execCount + 1);
158 }
159 pause(delay);
160 currentRequest = ClassicRequestBuilder.copy(scope.originalRequest).build();
161 continue;
162 }
163 if (ex instanceof NoHttpResponseException) {
164 final NoHttpResponseException updatedex = new NoHttpResponseException(
165 target.toHostString() + " failed to respond");
166 updatedex.setStackTrace(ex.getStackTrace());
167 throw updatedex;
168 }
169 throw ex;
170 }
171 }
172 }
173
174 private static void pause(final TimeValue delay) throws InterruptedIOException {
175 if (TimeValue.isPositive(delay)) {
176 try {
177 delay.sleep();
178 } catch (final InterruptedException e) {
179 Thread.currentThread().interrupt();
180 throw new InterruptedIOException();
181 }
182 }
183 }
184
185 }