Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6523089

java.net.URI.relativize(java.net.URI) mishandles non-slash-terminated base URI

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • 6
    • core-libs
    • Fix Understood
    • x86
    • linux

      FULL PRODUCT VERSION :
      java version "1.6.0"
      Java(TM) SE Runtime Environment (build 1.6.0-b105)
      Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)


      ADDITIONAL OS VERSION INFORMATION :
      Linux pizango 2.6.11.2 #4 Thu Apr 20 14:01:02 BST 2006 i686 unknown unknown GNU/Linux

      A DESCRIPTION OF THE PROBLEM :
      In URI.relativize(URI) , when it has established that normalized paths must be compared, it then ensures that the base path ends in a slash '/'. It does this by adding a slash if not already present. This leads to an incorrect result if the new base path happens to be a prefix of the argument's path. It can be shown to be incorrect by resolving it against the original base, to produce a path that does not match the original relativize(URI) argument.

      (An alternative interpretation is that relativize(URI) is correct according to the implementation and to the example given in the documentation, but then the stated inverse relatiionship between relativize(...) and resolve(...) does not generally hold.)

      For example (considering paths only):

      Base ('this'): /path/to
      Target (argument 'uri'): /path/to/linked_resource

      The base path doesn't end in a slash, so one is added:

      Base: /path/to/

      This is detected as a prefix of the argument path, so it is removed to leave behind:

      Result: linked_resource

      Resolving this against the original base path takes the following steps:

      1. Get original base path: "/path/to"
      2. Remove trailing non-slash-terminated path element: "/path/"
      3. Append relative path: "/path/linked_resource"

      This final path does not match the path supplied to the original relativize() call, because relativize(URI) added a slash to the base, instead of removing trailing non-slash characters.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Generate an absolute base URI that does not end in a slash:

      Base: http://www.example.com/path/to

      Generate another URI by appending a slash and adding other path segments:

      Target: http://www.example.com/path/to/linked_resource

      Relativize the target against the base, then resolve the result against the base, and compare with the target. They should be the same, but are not.

      (The supplied code performs these steps for the given example.)

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The output should be:

      Base: http://www.example.com/path/to
      Target: http://www.example.com/path/to/linked_resource
      Result: to/linked_resource
      Restored: (PASS) http://www.example.com/path/to/linked_resource


      ACTUAL -
      The output is:

      Base: http://www.example.com/path/to
      Target: http://www.example.com/path/to/linked_resource
      Result: linked_resource
      Restored: (FAIL) http://www.example.com/path/linked_resource



      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.net.URI;

      public class RelativizeBug {
          public static void relativizeAssertion(String baseText,
      String targetText) {
      URI base = URI.create(baseText);
      URI target = URI.create(targetText);
      URI result = base.relativize(target);
      URI restored = base.resolve(result);
      System.out.println("Base:\t" + baseText);
      System.out.println("Target:\t" + targetText);
      System.out.println("Result:\t" + result);
      System.out.println("Restored: (" +
      (restored.equals(target) ? "PASS" : "FAIL") +
      ")\t" + restored);
      System.out.println();
          }

          public static void main(String[] args) throws Exception {
      relativizeAssertion("http://www.example.com/path/to",
      "http://www.example.com/path/to/linked_resource");
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Strip off the trailing non-slash characters manually before submitting to relativize(URI).

      Or write your own method. An example is shown below to relativize just the path components. It first removes from the target the longest initial sequence of slash-terminated path segments that also start the base. Then, for each remaining slash-terminated path segment in the base, it prefixes the target with "../". This approach avoids the bug because, in the test case, the final segment is incomplete (lacking a trailing slash), so it does not contribute to the result. It also deals with a related issue RFE #6226081, whereby URI.relativize(URI) does not recognize the similarity between paths with common prefixes.

          // Paths must be normalized.
          static String relativize(String basePath, String targetPathString) {
              // We modify targetPath to become the result.
      StringBuilder targetPath = new StringBuilder(targetPathString);

      // Find the longest common initial sequence of path elements.
      int length = Math.min(basePath.length(), targetPath.length());
      int diff = 0;
      for (int i = 0; i < length; i++) {
      char c = basePath.charAt(i);
      if (c != targetPath.charAt(i))
      break;
      if (c == '/')
      diff = i + 1;
      }

      // Remove the common initial elements from the target, including
      // their trailing slashes.
      targetPath.delete(0, diff);

      // Count remaining complete path elements in the base,
      // prefixing the target with "../" for each one.
      for (int slash = basePath.indexOf('/', diff); slash > -1;
      slash = basePath.indexOf('/', slash + 1))
      targetPath.insert(0, "../");

      // Make sure the result is not empty.
      if (targetPath.length() == 0)
      targetPath.append("./");

              return targetPath.toString();
          }

            dfuchs Daniel Fuchs
            ryeung Roger Yeung (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Imported:
              Indexed: