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
28 package org.apache.http.impl.execchain;
29
30 import java.io.IOException;
31 import java.net.URI;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.http.HttpEntityEnclosingRequest;
36 import org.apache.http.HttpException;
37 import org.apache.http.HttpHost;
38 import org.apache.http.HttpRequest;
39 import org.apache.http.ProtocolException;
40 import org.apache.http.annotation.ThreadSafe;
41 import org.apache.http.auth.AuthScheme;
42 import org.apache.http.auth.AuthState;
43 import org.apache.http.client.RedirectException;
44 import org.apache.http.client.RedirectStrategy;
45 import org.apache.http.client.config.RequestConfig;
46 import org.apache.http.client.methods.CloseableHttpResponse;
47 import org.apache.http.client.methods.HttpExecutionAware;
48 import org.apache.http.client.methods.HttpRequestWrapper;
49 import org.apache.http.client.protocol.HttpClientContext;
50 import org.apache.http.client.utils.URIUtils;
51 import org.apache.http.conn.routing.HttpRoute;
52 import org.apache.http.conn.routing.HttpRoutePlanner;
53 import org.apache.http.util.Args;
54 import org.apache.http.util.EntityUtils;
55
56
57
58
59 @ThreadSafe
60 public class RedirectExec implements ClientExecChain {
61
62 private final Log log = LogFactory.getLog(getClass());
63
64 private final ClientExecChain requestExecutor;
65 private final RedirectStrategy redirectStrategy;
66 private final HttpRoutePlanner routePlanner;
67
68 public RedirectExec(
69 final ClientExecChain requestExecutor,
70 final HttpRoutePlanner routePlanner,
71 final RedirectStrategy redirectStrategy) {
72 super();
73 Args.notNull(requestExecutor, "HTTP client request executor");
74 Args.notNull(routePlanner, "HTTP route planner");
75 Args.notNull(redirectStrategy, "HTTP redirect strategy");
76 this.requestExecutor = requestExecutor;
77 this.routePlanner = routePlanner;
78 this.redirectStrategy = redirectStrategy;
79 }
80
81 public CloseableHttpResponse execute(
82 final HttpRoute route,
83 final HttpRequestWrapper request,
84 final HttpClientContext context,
85 final HttpExecutionAware execAware) throws IOException, HttpException {
86 Args.notNull(route, "HTTP route");
87 Args.notNull(request, "HTTP request");
88 Args.notNull(context, "HTTP context");
89
90 final RequestConfig config = context.getRequestConfig();
91 final int maxRedirects = config.getMaxRedirects() > 0 ? config.getMaxRedirects() : 50;
92 HttpRoute currentRoute = route;
93 HttpRequestWrapper currentRequest = request;
94 for (int redirectCount = 0;;) {
95 final CloseableHttpResponse response = requestExecutor.execute(
96 currentRoute, currentRequest, context, execAware);
97 try {
98 if (config.isRedirectsEnabled() &&
99 this.redirectStrategy.isRedirected(currentRequest, response, context)) {
100
101 if (redirectCount >= maxRedirects) {
102 throw new RedirectException("Maximum redirects ("+ maxRedirects + ") exceeded");
103 }
104 redirectCount++;
105
106 final HttpRequest redirect = this.redirectStrategy.getRedirect(currentRequest, response, context);
107 final HttpRequest original = currentRequest.getOriginal();
108 currentRequest = HttpRequestWrapper.wrap(redirect);
109 currentRequest.setHeaders(original.getAllHeaders());
110 if (original instanceof HttpEntityEnclosingRequest) {
111 Proxies.enhanceEntity((HttpEntityEnclosingRequest) request);
112 }
113
114 final URI uri = currentRequest.getURI();
115 final HttpHost newTarget = URIUtils.extractHost(uri);
116 if (uri.getHost() == null) {
117 throw new ProtocolException("Redirect URI does not specify a valid host name: " +
118 uri);
119 }
120
121
122 if (!currentRoute.getTargetHost().equals(newTarget)) {
123 final AuthState targetAuthState = context.getTargetAuthState();
124 if (targetAuthState != null) {
125 this.log.debug("Resetting target auth state");
126 targetAuthState.reset();
127 }
128 final AuthState proxyAuthState = context.getProxyAuthState();
129 if (proxyAuthState != null) {
130 final AuthScheme authScheme = proxyAuthState.getAuthScheme();
131 if (authScheme != null && authScheme.isConnectionBased()) {
132 this.log.debug("Resetting proxy auth state");
133 proxyAuthState.reset();
134 }
135 }
136 }
137
138 currentRoute = this.routePlanner.determineRoute(newTarget, currentRequest, context);
139 if (this.log.isDebugEnabled()) {
140 this.log.debug("Redirecting to '" + uri + "' via " + currentRoute);
141 }
142 EntityUtils.consume(response.getEntity());
143 response.close();
144 } else {
145 return response;
146 }
147 } catch (final RuntimeException ex) {
148 response.close();
149 throw ex;
150 } catch (final IOException ex) {
151 response.close();
152 throw ex;
153 } catch (final HttpException ex) {
154
155
156 try {
157 EntityUtils.consume(response.getEntity());
158 } catch (final IOException ioex) {
159 this.log.debug("I/O error while releasing connection", ioex);
160 } finally {
161 response.close();
162 }
163 throw ex;
164 }
165 }
166 }
167
168 }