You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ol...@apache.org on 2004/04/12 12:30:47 UTC
cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient TestRedirects.java TestNoHost.java TestWebappRedirect.java
olegk 2004/04/12 03:30:46
Modified: httpclient/src/java/org/apache/commons/httpclient
HttpMethodDirector.java
httpclient/src/java/org/apache/commons/httpclient/params
HttpClientParams.java
httpclient/src/test/org/apache/commons/httpclient
TestNoHost.java TestWebappRedirect.java
Added: httpclient/src/java/org/apache/commons/httpclient
RedirectException.java
httpclient/src/test/org/apache/commons/httpclient
TestRedirects.java
Log:
PR #21216 (Redirect 302 to the same URL causes max redirects exception)
Changelog:
* Circular redirect check added
* Maximum redirect attempts can now be changed by setting a parameter in the HttpParams collection
* The circular redirect check is on per default and in lenient mode, is off in
strict mode
* Authentication attempts excluded from the redirect count and do not affect the
max redirect check.
Contributed by Oleg Kalnichevski
Reviewed by Michael Becke
Revision Changes Path
1.20 +34 -28 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodDirector.java
Index: HttpMethodDirector.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodDirector.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- HttpMethodDirector.java 25 Mar 2004 20:37:19 -0000 1.19
+++ HttpMethodDirector.java 12 Apr 2004 10:30:46 -0000 1.20
@@ -32,7 +32,9 @@
package org.apache.commons.httpclient;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import org.apache.commons.httpclient.auth.AuthChallengeException;
import org.apache.commons.httpclient.auth.AuthChallengeParser;
@@ -66,9 +68,6 @@
/** The proxy authenticate response header. */
public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
- /** Maximum number of redirects and authentications that will be followed */
- private static final int MAX_FORWARDS = 100;
-
private static final Log LOG = LogFactory.getLog(HttpMethodDirector.class);
private ConnectMethod connectMethod;
@@ -98,6 +97,8 @@
/** Authentication processor */
private AuthChallengeProcessor authProcessor = null;
+ private Set redirectLocations = null;
+
public HttpMethodDirector(
final HttpConnectionManager connectionManager,
final HostConfiguration hostConfiguration,
@@ -125,13 +126,9 @@
}
method.getParams().setDefaults(this.params);
try {
- int forwardCount = 0; //protect from an infinite loop
+ int maxRedirects = this.params.getIntParameter(HttpClientParams.MAX_REDIRECTS, 100);
- while (forwardCount++ < MAX_FORWARDS) {
- // on every retry, reset this state information.
- if (LOG.isDebugEnabled()) {
- LOG.debug("Execute loop try " + forwardCount);
- }
+ for (int redirectCount = 0;;) {
// make sure the connection we have is appropriate
if (this.conn != null && !hostConfiguration.hostEquals(this.conn)) {
@@ -169,6 +166,15 @@
if (isRedirectNeeded(method)) {
if (processRedirectResponse(method)) {
retry = true;
+ ++redirectCount;
+ if (redirectCount >= maxRedirects) {
+ LOG.error("Narrowly avoided an infinite loop in execute");
+ throw new RedirectException("Maximum redirects ("
+ + maxRedirects + ") exceeded");
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Execute redirect " + redirectCount + " of " + maxRedirects);
+ }
}
}
if (isAuthenticationNeeded(method)) {
@@ -189,13 +195,6 @@
}
} //end of retry loop
-
- if (forwardCount >= MAX_FORWARDS) {
- LOG.error("Narrowly avoided an infinite loop in execute");
- throw new ProtocolException("Maximum redirects ("
- + MAX_FORWARDS + ") exceeded");
- }
-
} finally {
if (this.conn != null) {
this.conn.setLocked(false);
@@ -279,7 +278,6 @@
private void authenticateProxy(final HttpMethod method) throws AuthenticationException {
// Clean up existing authentication headers
- // Clean up existing authentication headers
if (!cleanAuthHeaders(method, PROXY_AUTH_RESP)) {
// User defined authentication header(s) present
return;
@@ -494,8 +492,9 @@
*
* @return <code>true</code> if the redirect was successful
*/
- private boolean processRedirectResponse(final HttpMethod method) {
-
+ private boolean processRedirectResponse(final HttpMethod method)
+ throws RedirectException
+ {
//get the location header to find out where to redirect to
Header locationHeader = method.getResponseHeader("location");
if (locationHeader == null) {
@@ -506,10 +505,9 @@
}
String location = locationHeader.getValue();
if (LOG.isDebugEnabled()) {
- LOG.debug("Redirect requested to location '" + location
- + "'");
+ LOG.debug("Redirect requested to location '" + location + "'");
}
-
+
//rfc2616 demands the location value be a complete URI
//Location = "Location" ":" absoluteURI
URI redirectUri = null;
@@ -538,9 +536,17 @@
LOG.warn("Redirected location '" + location + "' is malformed");
return false;
}
- //update the current location with the redirect location.
- //avoiding use of URL.getPath() and URL.getQuery() to keep
- //jdk1.2 comliance.
+
+ if (this.params.isParameterFalse(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS)) {
+ if (this.redirectLocations == null) {
+ this.redirectLocations = new HashSet();
+ }
+ this.redirectLocations.add(currentUri);
+ if (this.redirectLocations.contains(redirectUri)) {
+ throw new RedirectException("Circular redirect to '" +
+ redirectUri + "'");
+ }
+ }
method.setPath(redirectUri.getEscapedPath());
method.setQueryString(redirectUri.getEscapedQuery());
hostConfiguration.setHost(redirectUri);
1.1 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/RedirectException.java
Index: RedirectException.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/RedirectException.java,v 1.1 2004/04/12 10:30:46 olegk Exp $
* $Revision: 1.1 $
* $Date: 2004/04/12 10:30:46 $
*
* ====================================================================
*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed 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
* <http://www.apache.org/>.
*
*/
package org.apache.commons.httpclient;
/**
* Signals violation of HTTP specification caused by an invalid redirect
*
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*
* @since 3.0
*/
public class RedirectException extends ProtocolException {
/**
* Creates a new RedirectException with a <tt>null</tt> 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 <tt>Throwable</tt> that caused this exception, or <tt>null</tt>
* if the cause is unavailable, unknown, or not a <tt>Throwable</tt>
*/
public RedirectException(String message, Throwable cause) {
super(message, cause);
}
}
1.5 +33 -5 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/params/HttpClientParams.java
Index: HttpClientParams.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/params/HttpClientParams.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- HttpClientParams.java 22 Feb 2004 18:08:48 -0000 1.4
+++ HttpClientParams.java 12 Apr 2004 10:30:46 -0000 1.5
@@ -54,7 +54,9 @@
* Sets the timeout in milliseconds used when retrieving an
* {@link org.apache.commons.httpclient.HttpConnection HTTP connection} from the
* {@link org.apache.commons.httpclient.HttpConnectionManager HTTP connection manager}.
+ * <p>
* This parameter expects a value of type {@link Long}.
+ * </p>
*/
public static final String CONNECTION_MANAGER_TIMEOUT = "http.connection-manager.timeout";
@@ -62,22 +64,47 @@
* Defines the default
* {@link org.apache.commons.httpclient.HttpConnectionManager HTTP connection manager}
* class.
+ * <p>
* This parameter expects a value of type {@link Class}.
+ * </p>
*/
public static final String CONNECTION_MANAGER_CLASS = "http.connection-manager.class";
/**
* Defines whether authentication should be attempted preemptively.
+ * <p>
* This parameter expects a value of type {@link Boolean}.
+ * </p>
*/
public static final String PREEMPTIVE_AUTHENTICATION = "http.authentication.preemptive";
/**
* Defines whether relative redirects should be rejected.
+ * <p>
* This parameter expects a value of type {@link Boolean}.
+ * </p>
*/
public static final String REJECT_RELATIVE_REDIRECT = "http.protocol.reject-relative-redirect";
+ /**
+ * Defines the maximum number of redirects to be followed.
+ * The limit on number of redirects is intended to prevent infinite loops.
+ * <p>
+ * This parameter expects a value of type {@link Integer}.
+ * </p>
+ */
+ public static final String MAX_REDIRECTS = "http.protocol.max-redirects";
+
+ /**
+ * Defines whether circular redirects (redirects to the same location) should be allowed.
+ * The HTTP spec is not sufficiently clear whether circular redirects are permitted,
+ * therefore optionally they can be enabled
+ * <p>
+ * This parameter expects a value of type {@link Boolean}.
+ * </p>
+ */
+ public static final String ALLOW_CIRCULAR_REDIRECTS = "http.protocol.allow-circular-redirects";
+
/**
* Creates a new collection of parameters with the collection returned
* by {@link #getDefaultParams()} as a parent. The collection will defer
@@ -171,7 +198,8 @@
}
private static final String[] PROTOCOL_STRICTNESS_PARAMETERS = {
- REJECT_RELATIVE_REDIRECT
+ REJECT_RELATIVE_REDIRECT,
+ ALLOW_CIRCULAR_REDIRECTS
};
1.34 +5 -4 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestNoHost.java
Index: TestNoHost.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestNoHost.java,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -r1.33 -r1.34
--- TestNoHost.java 25 Mar 2004 20:37:20 -0000 1.33
+++ TestNoHost.java 12 Apr 2004 10:30:46 -0000 1.34
@@ -66,6 +66,7 @@
suite.addTest(TestChallengeProcessor.suite());
suite.addTest(TestAuthenticator.suite());
suite.addTest(TestBasicAuth.suite());
+ suite.addTest(TestRedirects.suite());
suite.addTest(TestHttpUrlMethod.suite());
suite.addTest(TestURI.suite());
suite.addTest(TestURIUtil.suite());
1.22 +5 -5 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappRedirect.java
Index: TestWebappRedirect.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappRedirect.java,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -r1.21 -r1.22
--- TestWebappRedirect.java 22 Feb 2004 18:08:50 -0000 1.21
+++ TestWebappRedirect.java 12 Apr 2004 10:30:46 -0000 1.22
@@ -179,7 +179,7 @@
try {
client.executeMethod(method);
fail("Expected HTTPException");
- } catch (HttpException t) {
+ } catch (ProtocolException t) {
// expected
}
assertEquals(302,method.getStatusCode());
1.1 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestRedirects.java
Index: TestRedirects.java
===================================================================
/*
* $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestRedirects.java,v 1.1 2004/04/12 10:30:46 olegk Exp $
* $Revision: 1.1 $
* $Date: 2004/04/12 10:30:46 $
* ====================================================================
*
* Copyright 1999-2004 The Apache Software Foundation
*
* Licensed 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.commons.httpclient;
import java.io.IOException;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.server.HttpService;
import org.apache.commons.httpclient.server.RequestLine;
import org.apache.commons.httpclient.server.SimpleRequest;
import org.apache.commons.httpclient.server.SimpleResponse;
/**
* Basic authentication test cases.
*
* @author Oleg Kalnichevski
*
* @version $Id: TestRedirects.java,v 1.1 2004/04/12 10:30:46 olegk Exp $
*/
public class TestRedirects extends HttpClientTestBase {
// ------------------------------------------------------------ Constructor
public TestRedirects(String testName) {
super(testName);
}
// ------------------------------------------------------------------- Main
public static void main(String args[]) {
String[] testCaseName = { TestRedirects.class.getName() };
junit.textui.TestRunner.main(testCaseName);
}
// ------------------------------------------------------- TestCase Methods
public static Test suite() {
return new TestSuite(TestRedirects.class);
}
private class RedirectService implements HttpService {
public RedirectService() {
super();
}
public boolean process(final SimpleRequest request, final SimpleResponse response)
throws IOException
{
RequestLine reqline = request.getRequestLine();
if (reqline.getUri().equals("/circular-location1/")) {
response.setStatusLine("HTTP/1.1 302 Object moved");
response.addHeader(new Header("Location", "/circular-location2/"));
} else if (reqline.getUri().equals("/circular-location2/")) {
response.setStatusLine("HTTP/1.1 302 Object moved");
response.addHeader(new Header("Location", "/circular-location1/"));
} else if (reqline.getUri().equals("/location1/")) {
response.setStatusLine("HTTP/1.1 302 Object moved");
response.addHeader(new Header("Location", "/location2/"));
} else if (reqline.getUri().equals("/location2/")) {
response.setStatusLine("HTTP/1.1 200 OK");
response.setBodyString("Successful redirect");
} else {
response.setStatusLine("HTTP/1.1 404 Not Found");
}
return true;
}
}
public void testMaxRedirectCheck() throws IOException {
this.server.setHttpService(new RedirectService());
GetMethod httpget = new GetMethod("/circular-location1/");
try {
this.client.getParams().setBooleanParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true);
this.client.getParams().setIntParameter(HttpClientParams.MAX_REDIRECTS, 5);
this.client.executeMethod(httpget);
fail("RedirectException exception should have been thrown");
}
catch (RedirectException e) {
// expected
} finally {
httpget.releaseConnection();
}
}
public void testCircularRedirect() throws IOException {
this.server.setHttpService(new RedirectService());
GetMethod httpget = new GetMethod("/circular-location1/");
try {
this.client.getParams().setBooleanParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, false);
this.client.executeMethod(httpget);
fail("RedirectException exception should have been thrown");
}
catch (RedirectException e) {
// expected
} finally {
httpget.releaseConnection();
}
}
public void testRedirectLocation() throws IOException {
this.server.setHttpService(new RedirectService());
GetMethod httpget = new GetMethod("/location1/");
try {
this.client.executeMethod(httpget);
assertEquals("/location2/", httpget.getPath());
assertEquals(new URI("/location2/", false), httpget.getURI());
System.out.println();
} finally {
httpget.releaseConnection();
}
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org