1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package org.apache.http.impl.client;
29
30 import java.io.IOException;
31 import java.io.InterruptedIOException;
32 import java.net.ConnectException;
33 import java.net.UnknownHostException;
34
35 import javax.net.ssl.SSLException;
36
37 import org.apache.http.annotation.Immutable;
38
39 import org.apache.http.HttpEntityEnclosingRequest;
40 import org.apache.http.HttpRequest;
41 import org.apache.http.client.HttpRequestRetryHandler;
42 import org.apache.http.protocol.HttpContext;
43 import org.apache.http.protocol.ExecutionContext;
44 import org.apache.http.client.methods.HttpUriRequest;
45
46 /**
47 * The default {@link HttpRequestRetryHandler} used by request executors.
48 *
49 *
50 * @since 4.0
51 */
52 @Immutable
53 public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
54
55 /** the number of times a method will be retried */
56 private final int retryCount;
57
58 /** Whether or not methods that have successfully sent their request will be retried */
59 private final boolean requestSentRetryEnabled;
60
61 /**
62 * Default constructor
63 */
64 public DefaultHttpRequestRetryHandler(int retryCount, boolean requestSentRetryEnabled) {
65 super();
66 this.retryCount = retryCount;
67 this.requestSentRetryEnabled = requestSentRetryEnabled;
68 }
69
70 /**
71 * Default constructor
72 */
73 public DefaultHttpRequestRetryHandler() {
74 this(3, false);
75 }
76 /**
77 * Used <code>retryCount</code> and <code>requestSentRetryEnabled</code> to determine
78 * if the given method should be retried.
79 */
80 public boolean retryRequest(
81 final IOException exception,
82 int executionCount,
83 final HttpContext context) {
84 if (exception == null) {
85 throw new IllegalArgumentException("Exception parameter may not be null");
86 }
87 if (context == null) {
88 throw new IllegalArgumentException("HTTP context may not be null");
89 }
90 if (executionCount > this.retryCount) {
91 // Do not retry if over max retry count
92 return false;
93 }
94 if (exception instanceof InterruptedIOException) {
95 // Timeout
96 return false;
97 }
98 if (exception instanceof UnknownHostException) {
99 // Unknown host
100 return false;
101 }
102 if (exception instanceof ConnectException) {
103 // Connection refused
104 return false;
105 }
106 if (exception instanceof SSLException) {
107 // SSL handshake exception
108 return false;
109 }
110
111 HttpRequest request = (HttpRequest)
112 context.getAttribute(ExecutionContext.HTTP_REQUEST);
113
114 if(requestIsAborted(request)){
115 return false;
116 }
117
118 if (handleAsIdempotent(request)) {
119 // Retry if the request is considered idempotent
120 return true;
121 }
122
123 Boolean b = (Boolean)
124 context.getAttribute(ExecutionContext.HTTP_REQ_SENT);
125 boolean sent = (b != null && b.booleanValue());
126
127 if (!sent || this.requestSentRetryEnabled) {
128 // Retry if the request has not been sent fully or
129 // if it's OK to retry methods that have been sent
130 return true;
131 }
132 // otherwise do not retry
133 return false;
134 }
135
136 /**
137 * @return <code>true</code> if this handler will retry methods that have
138 * successfully sent their request, <code>false</code> otherwise
139 */
140 public boolean isRequestSentRetryEnabled() {
141 return requestSentRetryEnabled;
142 }
143
144 /**
145 * @return the maximum number of times a method will be retried
146 */
147 public int getRetryCount() {
148 return retryCount;
149 }
150
151 /**
152 * @since 4.2
153 */
154 protected boolean handleAsIdempotent(final HttpRequest request) {
155 return !(request instanceof HttpEntityEnclosingRequest);
156 }
157
158 /**
159 * @since 4.2
160 */
161 protected boolean requestIsAborted(final HttpRequest request) {
162 HttpRequest req = request;
163 if (request instanceof RequestWrapper) { // does not forward request to original
164 req = ((RequestWrapper) request).getOriginal();
165 }
166 return (req instanceof HttpUriRequest && ((HttpUriRequest)req).isAborted());
167 }
168
169 }