diff --git a/src/java/org/apache/http/client/CircularRedirectException.java b/src/java/org/apache/http/client/CircularRedirectException.java new file mode 100644 index 000000000..d7f67f103 --- /dev/null +++ b/src/java/org/apache/http/client/CircularRedirectException.java @@ -0,0 +1,70 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client; + +/** + * Signals a circular redirect + * + * @author Oleg Kalnichevski + * + * @since 3.0 + */ +public class CircularRedirectException extends RedirectException { + + private static final long serialVersionUID = 6830063487001091803L; + + /** + * Creates a new CircularRedirectException with a null detail message. + */ + public CircularRedirectException() { + super(); + } + + /** + * Creates a new CircularRedirectException with the specified detail message. + * + * @param message The exception detail message + */ + public CircularRedirectException(String message) { + super(message); + } + + /** + * Creates a new CircularRedirectException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public CircularRedirectException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/java/org/apache/http/client/RedirectException.java b/src/java/org/apache/http/client/RedirectException.java new file mode 100644 index 000000000..63b18e67a --- /dev/null +++ b/src/java/org/apache/http/client/RedirectException.java @@ -0,0 +1,72 @@ +/* + * $HeadURL$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client; + +import org.apache.http.ProtocolException; + +/** + * Signals violation of HTTP specification caused by an invalid redirect + * + * @author Oleg Kalnichevski + * + * @since 3.0 + */ +public class RedirectException extends ProtocolException { + + private static final long serialVersionUID = 4418824536372559326L; + + /** + * Creates a new RedirectException with a null detail message. + */ + public RedirectException() { + super(); + } + + /** + * Creates a new RedirectException with the specified detail message. + * + * @param message The exception detail message + */ + public RedirectException(String message) { + super(message); + } + + /** + * Creates a new RedirectException with the specified detail message and cause. + * + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or null + * if the cause is unavailable, unknown, or not a Throwable + */ + public RedirectException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/java/org/apache/http/client/RedirectHandler.java b/src/java/org/apache/http/client/RedirectHandler.java index 7b3cbe0f7..8f5397f24 100644 --- a/src/java/org/apache/http/client/RedirectHandler.java +++ b/src/java/org/apache/http/client/RedirectHandler.java @@ -56,11 +56,12 @@ public interface RedirectHandler { * given the response from the target server. * * @param response the response received from the target server + * @param context the context for the request execution * * @return true if the request should be redirected, false * otherwise */ - boolean isRedirectNeeded(HttpResponse response); + boolean isRedirectNeeded(HttpResponse response, HttpContext context); /** * Determines the location request is expected to be redirected to diff --git a/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java b/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java index 68db08750..c91d75862 100644 --- a/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java +++ b/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java @@ -52,6 +52,7 @@ import org.apache.http.HttpVersion; import org.apache.http.ProtocolException; import org.apache.http.client.ClientRequestDirector; import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.RedirectException; import org.apache.http.client.RedirectHandler; import org.apache.http.client.RoutedRequest; import org.apache.http.client.methods.AbortableHttpRequest; @@ -115,7 +116,10 @@ public class DefaultClientRequestDirector /** The currently allocated connection. */ protected ManagedClientConnection managedConn; + private int redirectCount; + private int maxRedirects; + public DefaultClientRequestDirector( final ClientConnectionManager conman, final ConnectionReuseStrategy reustrat, @@ -151,6 +155,9 @@ public class DefaultClientRequestDirector this.requestExec = new HttpRequestExecutor(params); this.managedConn = null; + + this.redirectCount = 0; + this.maxRedirects = this.params.getIntParameter(HttpClientParams.MAX_REDIRECTS, 100); //@@@ authentication? @@ -238,7 +245,7 @@ public class DefaultClientRequestDirector } int execCount = 0; - + HttpResponse response = null; boolean done = false; try { @@ -516,8 +523,14 @@ public class DefaultClientRequestDirector HttpParams params = request.getParams(); if (params.getBooleanParameter(HttpClientParams.HANDLE_REDIRECTS, true) && - this.redirectHandler.isRedirectNeeded(response)) { + this.redirectHandler.isRedirectNeeded(response, context)) { + if (redirectCount >= maxRedirects) { + throw new RedirectException("Maximum redirects (" + + maxRedirects + ") exceeded"); + } + redirectCount++; + URI uri; try { uri = this.redirectHandler.getLocationURI(response, context); diff --git a/src/java/org/apache/http/impl/client/DefaultRedirectHandler.java b/src/java/org/apache/http/impl/client/DefaultRedirectHandler.java index f529939b8..df6aab150 100644 --- a/src/java/org/apache/http/impl/client/DefaultRedirectHandler.java +++ b/src/java/org/apache/http/impl/client/DefaultRedirectHandler.java @@ -33,6 +33,8 @@ package org.apache.http.impl.client; import java.net.URI; import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -41,6 +43,7 @@ import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.ProtocolException; +import org.apache.http.client.CircularRedirectException; import org.apache.http.client.RedirectHandler; import org.apache.http.client.params.HttpClientParams; import org.apache.http.params.HttpParams; @@ -60,8 +63,16 @@ import org.apache.http.protocol.HttpExecutionContext; public class DefaultRedirectHandler implements RedirectHandler { private static final Log LOG = LogFactory.getLog(DefaultRedirectHandler.class); + + private static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations"; - public boolean isRedirectNeeded(final HttpResponse response) { + public DefaultRedirectHandler() { + super(); + } + + public boolean isRedirectNeeded( + final HttpResponse response, + final HttpContext context) { if (response == null) { throw new IllegalArgumentException("HTTP response may not be null"); } @@ -131,6 +142,42 @@ public class DefaultRedirectHandler implements RedirectHandler { throw new ProtocolException(ex.getMessage(), ex); } } + + if (params.isParameterFalse(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS)) { + + Set redirectLocations = (Set) context.getAttribute(REDIRECT_LOCATIONS); + + if (redirectLocations == null) { + redirectLocations = new HashSet(); + context.setAttribute(REDIRECT_LOCATIONS, redirectLocations); + } + + URI redirectURI; + if (uri.getQuery() != null || uri.getFragment() != null) { + try { + redirectURI = new URI( + uri.getScheme(), + null, + uri.getHost(), + uri.getPort(), + uri.getPath(), + null, + null); + } catch (URISyntaxException ex) { + throw new ProtocolException(ex.getMessage(), ex); + } + } else { + redirectURI = uri; + } + + if (redirectLocations.contains(redirectURI)) { + throw new CircularRedirectException("Circular redirect to '" + + redirectURI + "'"); + } else { + redirectLocations.add(redirectURI); + } + } + return uri; }