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.protocol.HttpClientContext;
37 import org.apache.hc.client5.http.classic.ExecChainHandler;
38 import org.apache.hc.core5.annotation.Contract;
39 import org.apache.hc.core5.annotation.Internal;
40 import org.apache.hc.core5.annotation.ThreadingBehavior;
41 import org.apache.hc.core5.http.ClassicHttpRequest;
42 import org.apache.hc.core5.http.ClassicHttpResponse;
43 import org.apache.hc.core5.http.HttpEntity;
44 import org.apache.hc.core5.http.HttpException;
45 import org.apache.hc.core5.http.NoHttpResponseException;
46 import org.apache.hc.core5.util.Args;
47 import org.apache.hc.core5.util.TimeValue;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 @Contract(threading = ThreadingBehavior.STATELESS)
65 @Internal
66 public class HttpRequestRetryExec implements ExecChainHandler {
67
68 private static final Logger LOG = LoggerFactory.getLogger(HttpRequestRetryExec.class);
69
70 private final HttpRequestRetryStrategy retryStrategy;
71
72 public HttpRequestRetryExec(
73 final HttpRequestRetryStrategy retryStrategy) {
74 Args.notNull(retryStrategy, "retryStrategy");
75 this.retryStrategy = retryStrategy;
76 }
77
78 @Override
79 public ClassicHttpResponse execute(
80 final ClassicHttpRequest request,
81 final Scope scope,
82 final ExecChain chain) throws IOException, HttpException {
83 Args.notNull(request, "request");
84 Args.notNull(scope, "scope");
85 final String exchangeId = scope.exchangeId;
86 final HttpRoute route = scope.route;
87 final HttpClientContext context = scope.clientContext;
88 ClassicHttpRequest currentRequest = request;
89
90 for (int execCount = 1;; execCount++) {
91 final ClassicHttpResponse response;
92 try {
93 response = chain.proceed(currentRequest, scope);
94 } catch (final IOException ex) {
95 if (scope.execRuntime.isExecutionAborted()) {
96 throw new RequestFailedException("Request aborted");
97 }
98 final HttpEntity requestEntity = request.getEntity();
99 if (requestEntity != null && !requestEntity.isRepeatable()) {
100 if (LOG.isDebugEnabled()) {
101 LOG.debug("{}: cannot retry non-repeatable request", exchangeId);
102 }
103 throw ex;
104 }
105 if (retryStrategy.retryRequest(request, ex, execCount, context)) {
106 if (LOG.isDebugEnabled()) {
107 LOG.debug("{}: {}", exchangeId, ex.getMessage(), ex);
108 }
109 if (LOG.isInfoEnabled()) {
110 LOG.info("Recoverable I/O exception ({}) caught when processing request to {}",
111 ex.getClass().getName(), route);
112 }
113 currentRequest = ClassicRequestCopier.INSTANCE.copy(scope.originalRequest);
114 continue;
115 } else {
116 if (ex instanceof NoHttpResponseException) {
117 final NoHttpResponseException updatedex = new NoHttpResponseException(
118 route.getTargetHost().toHostString() + " failed to respond");
119 updatedex.setStackTrace(ex.getStackTrace());
120 throw updatedex;
121 }
122 throw ex;
123 }
124 }
125
126 try {
127 final HttpEntity entity = request.getEntity();
128 if (entity != null && !entity.isRepeatable()) {
129 if (LOG.isDebugEnabled()) {
130 LOG.debug("{}: cannot retry non-repeatable request", exchangeId);
131 }
132 return response;
133 }
134 if (retryStrategy.retryRequest(response, execCount, context)) {
135 response.close();
136 final TimeValue nextInterval =
137 retryStrategy.getRetryInterval(response, execCount, context);
138 if (TimeValue.isPositive(nextInterval)) {
139 try {
140 if (LOG.isDebugEnabled()) {
141 LOG.debug("{}: wait for {}", exchangeId, nextInterval);
142 }
143 nextInterval.sleep();
144 } catch (final InterruptedException e) {
145 Thread.currentThread().interrupt();
146 throw new InterruptedIOException();
147 }
148 }
149 currentRequest = ClassicRequestCopier.INSTANCE.copy(scope.originalRequest);
150 } else {
151 return response;
152 }
153 } catch (final RuntimeException ex) {
154 response.close();
155 throw ex;
156 }
157 }
158 }
159
160 }