Index: src/java/org/apache/commons/httpclient/URI.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/URI.java,v retrieving revision 1.34 diff -u -r1.34 URI.java --- src/java/org/apache/commons/httpclient/URI.java 5 Mar 2003 03:47:25 -0000 1.34 +++ src/java/org/apache/commons/httpclient/URI.java 10 Jun 2003 22:44:14 -0000 @@ -2923,7 +2923,7 @@ if (relPath == null || relPath.length == 0) { return normalize(basePath); } else if (relPath[0] == '/') { - return relPath; + return normalize(relPath); } else { StringBuffer buff = new StringBuffer(base.length() + relPath.length); @@ -3401,6 +3401,9 @@ /** * Normalize the given hier path part. + * + *

Algorithm taken from URI reference parser at + * http://www.apache.org/~fielding/uri/rev-2002/issues.html. * * @param path the path to normalize * @return the normalized path @@ -3413,62 +3416,69 @@ } String normalized = new String(path); - boolean endsWithSlash = true; - // precondition - if (!normalized.endsWith("/")) { - normalized += '/'; - endsWithSlash = false; - } - if (normalized.endsWith("/./") || normalized.endsWith("/../")) { - endsWithSlash = true; - } - // Resolve occurrences of "/./" in the normalized path - while (true) { - int at = normalized.indexOf("/./"); - if (at == -1) { - break; + + // If the buffer begins with "./" or "../", the "." or ".." is removed. + if (normalized.startsWith("./")) { + normalized = normalized.substring(1); + } else if (normalized.startsWith("../")) { + normalized = normalized.substring(2); + } else if (normalized.startsWith("..")) { + normalized = normalized.substring(2); + } + + // All occurrences of "/./" in the buffer are replaced with "/" + int index = -1; + while ((index = normalized.indexOf("/./")) != -1) { + normalized = normalized.substring(0, index) + normalized.substring(index + 2); + } + + // If the buffer ends with "/.", the "." is removed. + if (normalized.endsWith("/.")) { + normalized = normalized.substring(0, normalized.length() - 1); + } + + int startIndex = 0; + + // All occurrences of "//../" in the buffer, where ".." + // and are complete path segments, are iteratively replaced + // with "/" in order from left to right until no matching pattern remains. + // If the buffer ends with "//..", that is also replaced + // with "/". Note that may be empty. + while ((index = normalized.indexOf("/../", startIndex)) != -1) { + int slashIndex = normalized.lastIndexOf('/', index - 1); + if (slashIndex >= 0) { + normalized = normalized.substring(0, slashIndex) + normalized.substring(index + 3); + } else { + startIndex = index + 3; } - normalized = normalized.substring(0, at) - + normalized.substring(at + 2); } - while (true) { - // Resolve occurrences of "//" in the normalized path - int at = normalized.indexOf("//"); - if (at != -1) { - normalized = normalized.substring(0, at) - + normalized.substring(at + 1); - continue; + if (normalized.endsWith("/..")) { + int slashIndex = normalized.lastIndexOf('/', normalized.length() - 4); + if (slashIndex >= 0) { + normalized = normalized.substring(0, slashIndex + 1); } - // Resolve occurrences of "/../" in the normalized path - at = normalized.indexOf("/../"); - if (at == -1) { + } + + // All prefixes of "/../" in the buffer, where ".." + // and are complete path segments, are iteratively replaced + // with "/" in order from left to right until no matching pattern remains. + // If the buffer ends with "/..", that is also replaced + // with "/". Note that may be empty. + while ((index = normalized.indexOf("/../")) != -1) { + int slashIndex = normalized.lastIndexOf('/', index - 1); + if (slashIndex >= 0) { break; - } - if (at == 0) { - // no more higher path level to be normalized - if (!endsWithSlash && normalized.endsWith("/")) { - normalized = - normalized.substring(0, normalized.length() - 1); - } else if (endsWithSlash && !normalized.endsWith("/")) { - normalized = normalized + "/"; - } - throw new URIException(URIException.PARSING, new String(path)); - } - int backward = normalized.lastIndexOf('/', at - 1); - if (backward == -1) { - // consider the rel_path - normalized = normalized.substring(at + 4); } else { - normalized = normalized.substring(0, backward) - + normalized.substring(at + 3); + normalized = normalized.substring(index + 3); } } - if (!endsWithSlash && normalized.endsWith("/")) { - normalized = normalized.substring(0, normalized.length() - 1); - } else if (endsWithSlash && !normalized.endsWith("/")) { - normalized = normalized + "/"; + if (normalized.endsWith("/..")) { + int slashIndex = normalized.lastIndexOf('/', normalized.length() - 4); + if (slashIndex < 0) { + normalized = "/"; + } } - // Set the normalized path that we have completed + return normalized.toCharArray(); } Index: src/test/org/apache/commons/httpclient/TestURI.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestURI.java,v retrieving revision 1.3 diff -u -r1.3 TestURI.java --- src/test/org/apache/commons/httpclient/TestURI.java 30 Jan 2003 21:44:56 -0000 1.3 +++ src/test/org/apache/commons/httpclient/TestURI.java 10 Jun 2003 22:44:17 -0000 @@ -130,12 +130,10 @@ { "../..", "http", "a", "/", null, null, "http://a/" }, { "../../", "http", "a", "/", null, null, "http://a/" }, { "../../g", "http", "a", "/g", null, null, "http://a/g" }, - // Abnormal examples - // exception thrown the right below two - // { "../../../g", "http", "a", being-normalized path?, null, null, "http://a/../g?" }, - // { "../../../../g", "http", "a", being-normalied path?, null, null, "http://a/../../g?" }, - { "/./g", "http", "a", "/./g", null, null, "http://a/./g" }, - { "/../g", "http", "a", "/../g", null, null, "http://a/../g" }, + { "../../../g", "http", "a", "/g", null, null, "http://a/g" }, + { "../../../../g", "http", "a", "/g", null, null, "http://a/g" }, + { "/./g", "http", "a", "/g", null, null, "http://a/g" }, + { "/../g", "http", "a", "/g", null, null, "http://a/g" }, { "g.", "http", "a", "/b/c/g.", null, null, "http://a/b/c/g." }, { ".g", "http", "a", "/b/c/.g", null, null, "http://a/b/c/.g" }, { "g..", "http", "a", "/b/c/g..", null, null, "http://a/b/c/g.." }, @@ -152,12 +150,12 @@ { "g#s/../x", "http", "a", "/b/c/g", null, "s/../x", "http://a/b/c/g#s/../x" } }; for (int i = 0; i < testRelativeURIs.length; i++) { - URI testURI = null; try { testURI = new URI( baseURI, testRelativeURIs[i][0] ); } catch ( URIException e ) { + e.printStackTrace(); fail( "unable to create URI with relative value(" + testRelativeURIs[i][0] + "): " + e