in httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/RedirectExec.java [94:241]
public ClassicHttpResponse execute(
final ClassicHttpRequest request,
final ExecChain.Scope scope,
final ExecChain chain) throws IOException, HttpException {
Args.notNull(request, "HTTP request");
Args.notNull(scope, "Scope");
final HttpClientContext context = scope.clientContext;
context.setRedirectLocations(null);
final RequestConfig config = context.getRequestConfigOrDefault();
final int maxRedirects = config.getMaxRedirects() > 0 ? config.getMaxRedirects() : 50;
ClassicHttpRequest currentRequest = request;
ExecChain.Scope currentScope = scope;
for (int redirectCount = 0;;) {
final String exchangeId = currentScope.exchangeId;
final ClassicHttpResponse response = chain.proceed(currentRequest, currentScope);
try {
if (config.isRedirectsEnabled() && this.redirectStrategy.isRedirected(request, response, context)) {
final HttpEntity requestEntity = request.getEntity();
if (requestEntity != null && !requestEntity.isRepeatable()) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} cannot redirect non-repeatable request", exchangeId);
}
return response;
}
if (redirectCount >= maxRedirects) {
throw new RedirectException("Maximum redirects (" + maxRedirects + ") exceeded");
}
redirectCount++;
final URI redirectUri = this.redirectStrategy.getLocationURI(currentRequest, response, context);
if (LOG.isDebugEnabled()) {
LOG.debug("{} redirect requested to location '{}'", exchangeId, redirectUri);
}
final HttpHost newTarget = URIUtils.extractHost(redirectUri);
if (newTarget == null) {
throw new ProtocolException("Redirect URI does not specify a valid host name: " +
redirectUri);
}
final RedirectLocations redirectLocations = context.getRedirectLocations();
if (!config.isCircularRedirectsAllowed()) {
if (redirectLocations.contains(redirectUri)) {
throw new CircularRedirectException("Circular redirect to '" + redirectUri + "'");
}
}
final int statusCode = response.getCode();
final ClassicRequestBuilder redirectBuilder;
switch (statusCode) {
case HttpStatus.SC_MOVED_PERMANENTLY:
case HttpStatus.SC_MOVED_TEMPORARILY:
if (Method.POST.isSame(request.getMethod())) {
redirectBuilder = ClassicRequestBuilder.get();
} else {
redirectBuilder = ClassicRequestBuilder.copy(currentScope.originalRequest);
}
break;
case HttpStatus.SC_SEE_OTHER:
if (!Method.GET.isSame(request.getMethod()) && !Method.HEAD.isSame(request.getMethod())) {
redirectBuilder = ClassicRequestBuilder.get();
} else {
redirectBuilder = ClassicRequestBuilder.copy(currentScope.originalRequest);
}
break;
default:
redirectBuilder = ClassicRequestBuilder.copy(currentScope.originalRequest);
}
redirectBuilder.setUri(redirectUri);
final ClassicHttpRequest redirect = redirectBuilder.build();
final HttpRoute currentRoute = currentScope.route;
final HttpHost currentHost = currentRoute.getTargetHost();
if (!redirectStrategy.isRedirectAllowed(currentHost, newTarget, redirect, context)) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} cannot redirect due to safety restrictions", exchangeId);
}
return response;
}
redirectLocations.add(redirectUri);
final HttpRoute newRoute;
if (!Objects.equals(currentHost, newTarget)) {
newRoute = this.routePlanner.determineRoute(newTarget, context);
if (!Objects.equals(currentRoute, newRoute)) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} new route required", exchangeId);
}
final AuthExchange targetAuthExchange = context.getAuthExchange(currentHost);
if (LOG.isDebugEnabled()) {
LOG.debug("{} resetting target auth state", exchangeId);
}
targetAuthExchange.reset();
final HttpHost proxyHost = currentRoute.getProxyHost();
if (proxyHost != null) {
final AuthExchange proxyAuthExchange = context.getAuthExchange(proxyHost);
if (proxyAuthExchange.isConnectionBased()) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} resetting proxy auth state", exchangeId);
}
proxyAuthExchange.reset();
}
}
}
} else {
newRoute = currentRoute;
}
if (LOG.isDebugEnabled()) {
LOG.debug("{} redirecting to '{}' via {}", exchangeId, redirectUri, newRoute);
}
currentScope = new ExecChain.Scope(
scope.exchangeId,
newRoute,
ClassicRequestBuilder.copy(redirect).build(),
scope.execRuntime,
scope.clientContext);
currentRequest = redirect;
RequestEntityProxy.enhance(currentRequest);
EntityUtils.consume(response.getEntity());
response.close();
} else {
return response;
}
} catch (final RuntimeException | IOException ex) {
response.close();
throw ex;
} catch (final HttpException ex) {
// Protocol exception related to a direct.
// The underlying connection may still be salvaged.
try {
EntityUtils.consume(response.getEntity());
} catch (final IOException ioex) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} I/O error while releasing connection", exchangeId, ioex);
}
} finally {
response.close();
}
throw ex;
}
}
}