diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 5aaeacab6..2328854d5 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -4,7 +4,8 @@ Changes since release 4.3 BETA1 * [HTTPCLIENT-1354] do not quote algorithm parameter in DIGEST auth response. Contributed by Oleg Kalnichevski -* [HTTPCLIENT-1351] Preserve last request URI in the execution context. +* [HTTPCLIENT-1351] Added utility method to resolve final location from original request, + target host and a list of redirects. Contributed by James Leigh * [HTTPCLIENT-1344] Userinfo credentials in URI should not default to preemptive BASIC diff --git a/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java b/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java old mode 100644 new mode 100755 index 2db4af9b3..355ad30d2 --- a/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java +++ b/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java @@ -28,11 +28,13 @@ package org.apache.http.client.utils; import java.net.URI; import java.net.URISyntaxException; +import java.util.List; import java.util.Locale; import java.util.Stack; import org.apache.http.HttpHost; import org.apache.http.annotation.Immutable; +import org.apache.http.client.URICollection; import org.apache.http.util.Args; import org.apache.http.util.TextUtils; @@ -349,6 +351,52 @@ public class URIUtils { return target; } + /** + * Derives the interpreted (absolute) URI that was used to generate the last + * request. This is done by extracting the request-uri and target origin for + * the last request and scanning all the redirect locations for the last + * fragment identifier, then combining the result into a {@link URI}. + * + * @param originalURI + * original request before any redirects + * @param target + * if the last URI is relative, it is resolved against this target, + * or null if not available. + * @param redirects + * collection of redirect locations since the original request + * or null if not available. + * @return interpreted (absolute) URI + */ + public static URI resolve( + final URI originalURI, + final HttpHost target, + final List redirects) throws URISyntaxException { + Args.notNull(originalURI, "Request URI"); + final URIBuilder uribuilder; + if (redirects == null || redirects.isEmpty()) { + uribuilder = new URIBuilder(originalURI); + } else { + uribuilder = new URIBuilder(redirects.get(redirects.size() - 1)); + String frag = uribuilder.getFragment(); + // read interpreted fragment identifier from redirect locations + for (int i = redirects.size() - 1; frag == null && i >= 0; i--) { + frag = redirects.get(i).getFragment(); + } + uribuilder.setFragment(frag); + } + // read interpreted fragment identifier from original request + if (uribuilder.getFragment() == null) { + uribuilder.setFragment(originalURI.getFragment()); + } + // last target origin + if (target != null && !uribuilder.isAbsolute()) { + uribuilder.setScheme(target.getSchemeName()); + uribuilder.setHost(target.getHostName()); + uribuilder.setPort(target.getPort()); + } + return uribuilder.build(); + } + /** * This class should not be instantiated. */ diff --git a/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java b/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java old mode 100644 new mode 100755 index 90ef993a9..dd4a4f7b9 --- a/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java +++ b/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java @@ -27,8 +27,19 @@ package org.apache.http.client.utils; import java.net.URI; +import java.util.Arrays; import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.HttpVersion; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.protocol.HttpCoreContext; import org.junit.Assert; import org.junit.Test; @@ -203,4 +214,68 @@ public class TestURIUtils { URIUtils.extractHost(new URI("http://localhost:;sessionid=stuff/abcd"))); } + @Test + public void testHttpLocationWithRelativeFragment() throws Exception { + final HttpHost target = new HttpHost("localhost", -1, "http"); + final URI requestURI = new URI("/stuff#blahblah"); + + final URI location = URIUtils.resolve(requestURI, target, null); + final URI expectedURI = new URIBuilder(requestURI) + .setHost(target.getHostName()) + .setScheme(target.getSchemeName()) + .build(); + Assert.assertEquals(expectedURI, location); + } + + @Test + public void testHttpLocationWithAbsoluteFragment() throws Exception { + final HttpHost target = new HttpHost("localhost", 80, "http"); + + final URI requestURI = new URIBuilder() + .setHost(target.getHostName()) + .setScheme(target.getSchemeName()) + .setPath("/stuff") + .setFragment("blahblah") + .build(); + + final URI location = URIUtils.resolve(requestURI, target, null); + final URI expectedURI = requestURI; + Assert.assertEquals(expectedURI, location); + } + + @Test + public void testHttpLocationRedirect() throws Exception { + final HttpHost target = new HttpHost("localhost", -1, "http"); + final URI requestURI = new URI("/People.htm#tim"); + + final URI redirect = new URI("http://localhost/people.html"); + + final URI location = URIUtils.resolve(requestURI, target, Arrays.asList(redirect)); + final URI expectedURI = new URIBuilder() + .setHost(target.getHostName()) + .setScheme(target.getSchemeName()) + .setPath("/people.html") + .setFragment("tim") + .build(); + Assert.assertEquals(expectedURI, location); + } + + @Test + public void testHttpLocationWithRedirectFragment() throws Exception { + final HttpHost target = new HttpHost("localhost", -1, "http"); + final URI requestURI = new URI("/~tim"); + + final URI redirect1 = new URI("http://localhost/People.htm#tim"); + final URI redirect2 = new URI("http://localhost/people.html"); + + final URI location = URIUtils.resolve(requestURI, target, Arrays.asList(redirect1, redirect2)); + final URI expectedURI = new URIBuilder() + .setHost(target.getHostName()) + .setScheme(target.getSchemeName()) + .setPath("/people.html") + .setFragment("tim") + .build(); + Assert.assertEquals(expectedURI, location); + } + } diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientRequestExecution.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientRequestExecution.java old mode 100644 new mode 100755 index e1300774e..2ca11505b --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientRequestExecution.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientRequestExecution.java @@ -41,10 +41,12 @@ import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.NonRepeatableRequestException; +import org.apache.http.client.URICollection; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.utils.URIBuilder; +import org.apache.http.client.utils.URIUtils; import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClients; @@ -256,8 +258,13 @@ public class TestClientRequestExecution extends IntegrationTestBase { Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); EntityUtils.consume(response.getEntity()); - final HttpRequest request = (HttpRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST); + final HttpRequest request = context.getRequest(); Assert.assertEquals("/stuff", request.getRequestLine().getUri()); + + final URICollection redirectLocations = context.getRedirectLocations(); + final URI location = URIUtils.resolve(uri, target, + redirectLocations != null ? redirectLocations.getAll() : null); + Assert.assertEquals(uri, location); } } diff --git a/src/docbkx/httpagent.xml b/src/docbkx/httpagent.xml old mode 100644 new mode 100755 index 94ab8c5be..fd647c6e0 --- a/src/docbkx/httpagent.xml +++ b/src/docbkx/httpagent.xml @@ -205,6 +205,23 @@ HttpUriRequest req = (HttpUriRequest) localContext.getAttribute( System.out.println("Target host: " + target); System.out.println("Final request URI: " + req.getURI()); // relative URI (no proxy used) System.out.println("Final request method: " + req.getMethod()); +]]> + The final interpreted absolute HTTP location can be built using the original request + and the context. The utility method URIUtils.resolve(URI,HttpHost,URICollection) + can be used to build the interpreted absolute URI used to generate the final request. + This method includes the last fragment identifier from the redirect requests or + the original request. +