You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2013/08/13 21:38:49 UTC
svn commit: r1513622 - in /httpcomponents/httpclient/trunk: ./
httpclient-cache/src/main/java/org/apache/http/impl/client/cache/
httpclient-cache/src/test/java/org/apache/http/impl/client/cache/
httpclient/src/main/java/org/apache/http/client/utils/ ht...
Author: olegk
Date: Tue Aug 13 19:38:48 2013
New Revision: 1513622
URL: http://svn.apache.org/r1513622
Log:
HTTPCLIENT-1385: Fixed path normalization in CacheKeyGenerator
Contributed by James Leigh <james at 3roundstones dot com>
Modified:
httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java
httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheKeyGenerator.java
httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java
httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java
Modified: httpcomponents/httpclient/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/RELEASE_NOTES.txt?rev=1513622&r1=1513621&r2=1513622&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpclient/trunk/RELEASE_NOTES.txt Tue Aug 13 19:38:48 2013
@@ -2,6 +2,9 @@
Changes since release 4.3 BETA2
-------------------
+* [HTTPCLIENT-1385] Fixed path normalization in CacheKeyGenerator
+ Contributed by James Leigh <james at 3roundstones dot com>
+
* [HTTPCLIENT-1370] Response to non-GET requests should never be cached with the default
ResponseCachingPolicy
Contributed by James Leigh <james at 3roundstones dot com>
Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java?rev=1513622&r1=1513621&r2=1513622&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java Tue Aug 13 19:38:48 2013
@@ -29,9 +29,7 @@ package org.apache.http.impl.client.cach
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
-import java.net.URISyntaxException;
import java.net.URL;
-import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
@@ -45,6 +43,7 @@ import org.apache.http.HttpRequest;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.utils.URIUtils;
/**
* @since 4.1
@@ -52,6 +51,8 @@ import org.apache.http.client.cache.Http
@Immutable
class CacheKeyGenerator {
+ private static final URI BASE_URI = URI.create("http://example.com/");
+
/**
* For a given {@link HttpHost} and {@link HttpRequest} get a URI from the
* pair that I can use as an identifier KEY into my HttpCache
@@ -69,33 +70,23 @@ class CacheKeyGenerator {
public String canonicalizeUri(final String uri) {
try {
- final URL u = new URL(uri);
- final String protocol = u.getProtocol().toLowerCase();
- final String hostname = u.getHost().toLowerCase();
+ final URI normalized = URIUtils.resolve(BASE_URI, uri);
+ final URL u = new URL(normalized.toASCIIString());
+ final String protocol = u.getProtocol();
+ final String hostname = u.getHost();
final int port = canonicalizePort(u.getPort(), protocol);
- String path = canonicalizePath(u.getPath());
- if ("".equals(path)) {
- path = "/";
- }
+ final String path = u.getPath();
final String query = u.getQuery();
final String file = (query != null) ? (path + "?" + query) : path;
final URL out = new URL(protocol, hostname, port, file);
return out.toString();
+ } catch (final IllegalArgumentException e) {
+ return uri;
} catch (final MalformedURLException e) {
return uri;
}
}
- private String canonicalizePath(final String path) {
- try {
- final String decoded = URLDecoder.decode(path, "UTF-8");
- return (new URI(decoded)).getPath();
- } catch (final UnsupportedEncodingException e) {
- } catch (final URISyntaxException e) {
- }
- return path;
- }
-
private int canonicalizePort(final int port, final String protocol) {
if (port == -1 && "http".equalsIgnoreCase(protocol)) {
return 80;
Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheKeyGenerator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheKeyGenerator.java?rev=1513622&r1=1513621&r2=1513622&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheKeyGenerator.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheKeyGenerator.java Tue Aug 13 19:38:48 2013
@@ -358,6 +358,46 @@ public class TestCacheKeyGenerator {
}
@Test
+ public void testExtraDotSegmentsAreIgnored() {
+ final HttpHost host = new HttpHost("foo.example.com");
+ final HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
+ final HttpRequest req2 = new HttpGet("http://foo.example.com/./");
+ Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
+ }
+
+ @Test
+ public void testExtraDotDotSegmentsAreIgnored() {
+ final HttpHost host = new HttpHost("foo.example.com");
+ final HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
+ final HttpRequest req2 = new HttpGet("http://foo.example.com/.././../");
+ Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
+ }
+
+ @Test
+ public void testIntermidateDotDotSegementsAreEquivalent() {
+ final HttpHost host = new HttpHost("foo.example.com");
+ final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html", HttpVersion.HTTP_1_1);
+ final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html", HttpVersion.HTTP_1_1);
+ Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
+ }
+
+ @Test
+ public void testIntermidateEncodedDotDotSegementsAreEquivalent() {
+ final HttpHost host = new HttpHost("foo.example.com");
+ final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html", HttpVersion.HTTP_1_1);
+ final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith%2F../home.html", HttpVersion.HTTP_1_1);
+ Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
+ }
+
+ @Test
+ public void testIntermidateDotSegementsAreEquivalent() {
+ final HttpHost host = new HttpHost("foo.example.com");
+ final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html", HttpVersion.HTTP_1_1);
+ final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/./home.html", HttpVersion.HTTP_1_1);
+ Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
+ }
+
+ @Test
public void testEquivalentPathEncodingsAreEquivalent() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html", HttpVersion.HTTP_1_1);
@@ -372,4 +412,12 @@ public class TestCacheKeyGenerator {
final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith%2Fhome.html", HttpVersion.HTTP_1_1);
Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
}
+
+ @Test
+ public void testEquivalentExtraPathEncodingsWithPercentAreEquivalent() {
+ final HttpHost host = new HttpHost("foo.example.com");
+ final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home%20folder.html", HttpVersion.HTTP_1_1);
+ final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith%2Fhome%20folder.html", HttpVersion.HTTP_1_1);
+ Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
+ }
}
Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java?rev=1513622&r1=1513621&r2=1513622&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java Tue Aug 13 19:38:48 2013
@@ -233,7 +233,7 @@ public class URIUtils {
resolved = URI.create(resolvedString.substring(0,
resolvedString.indexOf('#')));
}
- return removeDotSegments(resolved);
+ return normalizeSyntax(resolved);
}
/**
@@ -252,17 +252,18 @@ public class URIUtils {
}
/**
- * Removes dot segments according to RFC 3986, section 5.2.4
+ * Removes dot segments according to RFC 3986, section 5.2.4 and
+ * Syntax-Based Normalization according to RFC 3986, section 6.2.2.
*
* @param uri the original URI
* @return the URI without dot segments
*/
- private static URI removeDotSegments(final URI uri) {
- final String path = uri.getPath();
- if ((path == null) || (path.indexOf("/.") == -1)) {
- // No dot segments to remove
+ private static URI normalizeSyntax(final URI uri) {
+ if (uri.isOpaque()) {
return uri;
}
+ Args.check(uri.isAbsolute(), "Base URI must be absolute");
+ final String path = uri.getPath() == null ? "" : uri.getPath();
final String[] inputSegments = path.split("/");
final Stack<String> outputSegments = new Stack<String>();
for (final String inputSegment : inputSegments) {
@@ -281,9 +282,29 @@ public class URIUtils {
for (final String outputSegment : outputSegments) {
outputBuffer.append('/').append(outputSegment);
}
+ if (path.lastIndexOf('/') == path.length() - 1) {
+ // path.endsWith("/") || path.equals("")
+ outputBuffer.append('/');
+ }
try {
- return new URI(uri.getScheme(), uri.getAuthority(),
- outputBuffer.toString(), uri.getQuery(), uri.getFragment());
+ final String scheme = uri.getScheme().toLowerCase();
+ final String auth = uri.getAuthority().toLowerCase();
+ final URI ref = new URI(scheme, auth, outputBuffer.toString(),
+ null, null);
+ if (uri.getQuery() == null && uri.getFragment() == null) {
+ return ref;
+ }
+ final StringBuilder normalized = new StringBuilder(
+ ref.toASCIIString());
+ if (uri.getQuery() != null) {
+ // query string passed through unchanged
+ normalized.append('?').append(uri.getRawQuery());
+ }
+ if (uri.getFragment() != null) {
+ // fragment passed through unchanged
+ normalized.append('#').append(uri.getRawFragment());
+ }
+ return URI.create(normalized.toString());
} catch (final URISyntaxException e) {
throw new IllegalArgumentException(e);
}
Modified: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java?rev=1513622&r1=1513621&r2=1513622&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java Tue Aug 13 19:38:48 2013
@@ -101,13 +101,23 @@ public class TestURIUtils {
}
@Test
+ public void testNormalization() {
+ Assert.assertEquals("example://a/b/c/%7Bfoo%7D", URIUtils.resolve(this.baseURI, "eXAMPLE://a/./b/../b/%63/%7bfoo%7d").toString());
+ Assert.assertEquals("http://www.example.com/%3C", URIUtils.resolve(this.baseURI, "http://www.example.com/%3c").toString());
+ Assert.assertEquals("http://www.example.com/", URIUtils.resolve(this.baseURI, "HTTP://www.EXAMPLE.com/").toString());
+ Assert.assertEquals("http://www.example.com/a/", URIUtils.resolve(this.baseURI, "http://www.example.com/a%2f").toString());
+ Assert.assertEquals("http://www.example.com/?q=%26", URIUtils.resolve(this.baseURI, "http://www.example.com/?q=%26").toString());
+ Assert.assertEquals("http://www.example.com/%23?q=%26", URIUtils.resolve(this.baseURI, "http://www.example.com/%23?q=%26").toString());
+ }
+
+ @Test
public void testResolve() {
Assert.assertEquals("g:h", URIUtils.resolve(this.baseURI, "g:h").toString());
Assert.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "g").toString());
Assert.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "./g").toString());
Assert.assertEquals("http://a/b/c/g/", URIUtils.resolve(this.baseURI, "g/").toString());
Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/g").toString());
- Assert.assertEquals("http://g", URIUtils.resolve(this.baseURI, "//g").toString());
+ Assert.assertEquals("http://g/", URIUtils.resolve(this.baseURI, "//g").toString());
Assert.assertEquals("http://a/b/c/d;p?y", URIUtils.resolve(this.baseURI, "?y").toString());
Assert.assertEquals("http://a/b/c/d;p?y#f", URIUtils.resolve(this.baseURI, "?y#f")
.toString());