diff --git a/src/java/org/apache/http/client/RedirectHandler.java b/src/java/org/apache/http/client/RedirectHandler.java
new file mode 100644
index 000000000..7b3cbe0f7
--- /dev/null
+++ b/src/java/org/apache/http/client/RedirectHandler.java
@@ -0,0 +1,78 @@
+/*
+ * $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
+ *
+ * Classes implementing this interface must synchronize access to shared + * data as methods of this interfrace may be executed from multiple threads + *
+ * + * @author Oleg Kalnichevski + */ +public interface RedirectHandler { + + /** + * Determines if a request should be redirected to a new location + * given the response from the target server. + * + * @param response the response received from the target server + * + * @returntrue
if the request should be redirected, false
+ * otherwise
+ */
+ boolean isRedirectNeeded(HttpResponse response);
+
+ /**
+ * Determines the location request is expected to be redirected to
+ * given the response from the target server and the current request
+ * execution context.
+ *
+ * @param response the response received from the target server
+ * @param context the context for the request execution
+ *
+ * @return redirect URI
+ */
+ URI getLocationURI(HttpResponse response, HttpContext context)
+ throws ProtocolException;
+
+}
diff --git a/src/java/org/apache/http/client/params/HttpClientParams.java b/src/java/org/apache/http/client/params/HttpClientParams.java
index 46e4adfb9..9dbf0cc7a 100644
--- a/src/java/org/apache/http/client/params/HttpClientParams.java
+++ b/src/java/org/apache/http/client/params/HttpClientParams.java
@@ -69,6 +69,14 @@ public class HttpClientParams {
*/
public static final String PREEMPTIVE_AUTHENTICATION = "http.authentication.preemptive";
+ /**
+ * Defines whether redirects should be handled automatically
+ * + * This parameter expects a value of type {@link Boolean}. + *
+ */ + public static final String HANDLE_REDIRECTS = "http.protocol.handle-redirects"; + /** * Defines whether relative redirects should be rejected. *@@ -104,6 +112,15 @@ public class HttpClientParams { */ public static final String COOKIE_POLICY = "http.protocol.cookie-policy"; + /** + * Defines the request headers to be sent per default with each request. + *
+ * This parameter expects a value of type {@link java.util.Collection}. The + * collection is expected to contain {@link org.apache.http.Header}s. + *
+ */ + public static final String DEFAULT_HEADERS = "http.default-headers"; + private HttpClientParams() { super(); } diff --git a/src/java/org/apache/http/conn/HttpRoute.java b/src/java/org/apache/http/conn/HttpRoute.java index b3457f7ae..f1f117791 100644 --- a/src/java/org/apache/http/conn/HttpRoute.java +++ b/src/java/org/apache/http/conn/HttpRoute.java @@ -126,6 +126,17 @@ public final class HttpRoute { } + /** + * Creates a new direct insecure route. + * That is a route without a proxy. + * + * @param target the host to which to route + */ + public HttpRoute(HttpHost target) { + this(target, null, null, false, false, false); + } + + /** * Creates a new route through a proxy. * When using this constructor, theproxy
MUST be given.
diff --git a/src/java/org/apache/http/impl/client/AbstractHttpClient.java b/src/java/org/apache/http/impl/client/AbstractHttpClient.java
index b4016d8d8..56f88c0bb 100644
--- a/src/java/org/apache/http/impl/client/AbstractHttpClient.java
+++ b/src/java/org/apache/http/impl/client/AbstractHttpClient.java
@@ -47,6 +47,7 @@ import org.apache.http.client.ClientRequestDirector;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpState;
+import org.apache.http.client.RedirectHandler;
import org.apache.http.client.RoutedRequest;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
@@ -99,6 +100,9 @@ public abstract class AbstractHttpClient
/** The request retry handler. */
private HttpRequestRetryHandler retryHandler;
+ /** The redirect handler. */
+ private RedirectHandler redirectHandler;
+
/** The default HTTP state. */
private HttpState defaultState;
@@ -140,6 +144,9 @@ public abstract class AbstractHttpClient
protected abstract HttpRequestRetryHandler createHttpRequestRetryHandler();
+ protected abstract RedirectHandler createRedirectHandler();
+
+
protected abstract HttpState createHttpState();
@@ -231,6 +238,19 @@ public abstract class AbstractHttpClient
}
+ public synchronized final RedirectHandler getRedirectHandler() {
+ if (redirectHandler == null) {
+ redirectHandler = createRedirectHandler();
+ }
+ return redirectHandler;
+ }
+
+
+ public synchronized void setRedirectHandler(final RedirectHandler redirectHandler) {
+ this.redirectHandler = redirectHandler;
+ }
+
+
public synchronized final HttpState getState() {
if (defaultState == null) {
defaultState = createHttpState();
@@ -397,6 +417,7 @@ public abstract class AbstractHttpClient
getConnectionReuseStrategy(),
getHttpProcessor().copy(),
getHttpRequestRetryHandler(),
+ getRedirectHandler(),
getParams());
}
diff --git a/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java b/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java
index a9a2a7b8f..4ec97b536 100644
--- a/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java
+++ b/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java
@@ -32,12 +32,16 @@
package org.apache.http.impl.client;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
@@ -48,8 +52,10 @@ 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.RedirectHandler;
import org.apache.http.client.RoutedRequest;
import org.apache.http.client.methods.AbortableHttpRequest;
+import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.BasicManagedEntity;
import org.apache.http.conn.ClientConnectionManager;
@@ -57,6 +63,8 @@ import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.HttpRoute;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.RouteDirector;
+import org.apache.http.conn.Scheme;
+import org.apache.http.conn.SchemeRegistry;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
@@ -98,6 +106,9 @@ public class DefaultClientRequestDirector
/** The request retry handler. */
protected final HttpRequestRetryHandler retryHandler;
+ /** The redirect handler. */
+ protected final RedirectHandler redirectHandler;
+
/** The HTTP parameters. */
protected final HttpParams params;
@@ -110,6 +121,7 @@ public class DefaultClientRequestDirector
final ConnectionReuseStrategy reustrat,
final HttpProcessor httpProcessor,
final HttpRequestRetryHandler retryHandler,
+ final RedirectHandler redirectHandler,
final HttpParams params) {
if (conman == null) {
@@ -124,6 +136,9 @@ public class DefaultClientRequestDirector
if (retryHandler == null) {
throw new IllegalArgumentException("HTTP request retry handler may not be null");
}
+ if (redirectHandler == null) {
+ throw new IllegalArgumentException("Redirect handler may not be null");
+ }
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
@@ -131,6 +146,7 @@ public class DefaultClientRequestDirector
this.reuseStrategy = reustrat;
this.httpProcessor = httpProcessor;
this.retryHandler = retryHandler;
+ this.redirectHandler = redirectHandler;
this.params = params;
this.requestExec = new HttpRequestExecutor(params);
@@ -206,20 +222,35 @@ public class DefaultClientRequestDirector
public HttpResponse execute(RoutedRequest roureq, HttpContext context)
throws HttpException, IOException {
- RequestWrapper request = wrapRequest(roureq.getRequest());
+ HttpRequest orig = roureq.getRequest();
+
+ // Link parameter collections to form a hierarchy:
+ // request -> client
+ orig.getParams().setDefaults(this.params);
+
+ // Add default headers
+ Collection defHeaders = (Collection) orig.getParams().getParameter(
+ HttpClientParams.DEFAULT_HEADERS);
+ if (defHeaders != null) {
+ for (Iterator it = defHeaders.iterator(); it.hasNext(); ) {
+ orig.addHeader((Header) it.next());
+ }
+ }
+
+ int execCount = 0;
+
HttpResponse response = null;
boolean done = false;
-
try {
- int execCount = 0;
while (!done) {
+ RequestWrapper request = wrapRequest(roureq.getRequest());
HttpRoute route = roureq.getRoute();
// Re-write request URI if needed
rewriteRequestURI(request, route);
- if (managedConn == null) {
+ if (managedConn == null || !managedConn.isOpen()) {
managedConn = allocateConnection(route);
}
establishRoute(route, context);
@@ -465,9 +496,9 @@ public class DefaultClientRequestDirector
* Analyzes a response to check need for a followup.
*
* @param roureq the request and route. This is the same object as
- * was passed to {@link #prepareRequest prepareRequest}.
+ * was passed to {@link #wrapRequest(HttpRequest)}.
* @param request the request that was actually sent. This is the object
- * returned by {@link #prepareRequest prepareRequest}.
+ * returned by {@link #wrapRequest(HttpRequest)}.
* @param response the response to analayze
* @param context the context used for the current request execution
*
@@ -483,15 +514,50 @@ public class DefaultClientRequestDirector
HttpContext context)
throws HttpException, IOException {
- //@@@ if there is a followup, check connection keep-alive and
- //@@@ consume response body if necessary or close otherwise
+ HttpParams params = request.getParams();
+ if (params.getBooleanParameter(HttpClientParams.HANDLE_REDIRECTS, true) &&
+ this.redirectHandler.isRedirectNeeded(response)) {
- //@@@ if the request needs to be re-sent with authentication,
- //@@@ how to revert the modifications applied by the interceptors?
- //@@@ use a wrapper when sending?
+ URI uri;
+ try {
+ uri = this.redirectHandler.getLocationURI(response, context);
+ } catch (ProtocolException ex) {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn(ex.getMessage());
+ }
+ return null;
+ }
+ HttpRoute oldRoute = roureq.getRoute();
+
+ HttpHost newTarget = new HttpHost(
+ uri.getHost(),
+ uri.getPort(),
+ uri.getScheme());
+
+ SchemeRegistry schemeRegistry = this.connManager.getSchemeRegistry();
+ Scheme schm = schemeRegistry.getScheme(newTarget.getSchemeName());
+
+ InetAddress localAddress = oldRoute.getLocalAddress();
+ HttpHost proxy = oldRoute.getProxyHost();
+
+ HttpRoute newRoute = new HttpRoute(
+ newTarget,
+ localAddress,
+ proxy,
+ schm.isLayered(),
+ (proxy != null),
+ (proxy != null));
+
+ HttpGet redirect = new HttpGet(uri);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Redirecting to '" + uri + "' via " + newRoute);
+ }
+
+ return new RoutedRequest.Impl(redirect, newRoute);
+ }
return null;
-
} // handleResponse
diff --git a/src/java/org/apache/http/impl/client/DefaultHttpClient.java b/src/java/org/apache/http/impl/client/DefaultHttpClient.java
index 21f80f45e..fc9d51ddf 100644
--- a/src/java/org/apache/http/impl/client/DefaultHttpClient.java
+++ b/src/java/org/apache/http/impl/client/DefaultHttpClient.java
@@ -39,6 +39,7 @@ import org.apache.http.HttpVersion;
import org.apache.http.auth.AuthSchemeRegistry;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpState;
+import org.apache.http.client.RedirectHandler;
import org.apache.http.client.RoutedRequest;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.client.params.CookiePolicy;
@@ -211,6 +212,11 @@ public class DefaultHttpClient extends AbstractHttpClient {
}
+ protected RedirectHandler createRedirectHandler() {
+ return new DefaultRedirectHandler();
+ }
+
+
protected HttpState createHttpState() {
return new HttpState();
}
diff --git a/src/java/org/apache/http/impl/client/DefaultRedirectHandler.java b/src/java/org/apache/http/impl/client/DefaultRedirectHandler.java
new file mode 100644
index 000000000..f529939b8
--- /dev/null
+++ b/src/java/org/apache/http/impl/client/DefaultRedirectHandler.java
@@ -0,0 +1,137 @@
+/*
+ * $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
+ *