When the relative URI contains an empty path, the URI of Java Is resolve incompatible with RFC 3986?

I believe in Java URIs The definition and implementation of resolve method are the same as RFC 3986 Section 5.2 2 incompatible I know that the Java API defines the working principle of this method. If it is changed now, it will destroy the existing application, but my question is: anyone can confirm that my understanding is that this method is incompatible with RFC 3986?

I'm using an example of this problem: Java net. Uri resolve against only query string, I will copy here:

I am using JDK Java net. Uri to build URI I want to attach to an absolute URI object, a query (in string) For example:

URI base = new URI("http://example.com/something/more/long");
String queryString = "query=http://local:282/rand&action=aaaa";
URI query = new URI(null,null,queryString,null);
URI result = base.resolve(query);

The theory (or I think) is that such a determination should return:

http://example.com/something/more/long?query=http://local:282/rand&action=aaaa

But what I get is:

http://example.com/something/more/?query=http://local:282/rand&action=aaaa

I am satisfied with RFC 3986 Section 5.2 2 understands that if the path of the relative URI is empty, the entire path of the basic URI is used:

if (R.path == "") then
           T.path = Base.path;
           if defined(R.query) then
              T.query = R.query;
           else
              T.query = Base.query;
           endif;

And only the specified path is the relative path to be merged with the base path:

else
           if (R.path starts-with "/") then
              T.path = remove_dot_segments(R.path);
           else
              T.path = merge(Base.path,R.path);
              T.path = remove_dot_segments(T.path);
           endif;
           T.query = R.query;
        endif;

However, the Java implementation always performs the merge, even if the path is empty:

String cp = (child.path == null) ? "" : child.path;
    if ((cp.length() > 0) && (cp.charAt(0) == '/')) {
      // 5.2 (5): Child path is absolute
      ru.path = child.path;
    } else {
      // 5.2 (6): Resolve relative path
      ru.path = resolvePath(base.path,cp,base.isAbsolute());
    }

If my reading is correct, to obtain this behavior from the RFC pseudo code, you can take a point as the path in the relative URI before querying the string. From my experience of using the relative URI as a web page link, I expect:

transform(Base="http://example.com/something/more/long",R=".?query")
    => T="http://example.com/something/more/?query"

But I would expect a page on a web page“ http://example.com/something/more/long ”The link to query goes to“ http://example.com/something/more/long?query ”, not“ http://example.com/something/more/?query ”– in other words, it is consistent with RFC, but not with Java implementation

Did I read the RFC correctly, did the Java method disagree with it, or did I miss something?

Solution

Yes, I agree The resolve (URI) method is incompatible with RFC 3986 The original question itself provides a lot of research to help this conclusion First, let's clear up any confusion

As raedwald explained (in the answer deleted now), the basic path ending with / or not ending with / is different:

>Fizz relative to / foo / bar yes: / foo / fizz > fizz relative to / foo / bar / Yes: / foo / bar / Fizz

Although correct, this is not a complete answer, because the original question is not to ask a path (i.e. "fizz" above) Instead, the problem involves a separate query component referenced by a relative URI The URI class constructor used in the example code accepts five different string parameters. All parameters except querystring parameter are passed as null (note that Java accepts an empty string as a path parameter, which logically leads to an "empty" path component because "the path component is never undefined" although it is "may be empty (zero length)".) This will be important in the future

In earlier comment, sajan chandran pointed out that Java net. The URI class is recorded as a problem implementing RFC 2396 instead of RFC 3986 The former was abandoned by the latter in 2005 The URI class Javadoc does not mention that the new RFC can be interpreted as more evidence of its incompatibility Let's add a little more:

>Jdk-6791060 is an open problem. It is suggested that this class "should be updated to RFC 3986" A comment there warned that "rfc3986 is not fully backward compatible with 2396." > previously, I tried to update the part of the URI class to comply with RFC 3986 (e.g. jdk-6348622), but later the compatibility was rolled back. (see also this discussion in the JDK mailing list) > although the path "merge" logic sounds similar, as shown by noted by suboptimal, The pseudocode specified in the newer RFC does not match the actual implementation In pseudo code, when the path of the relative URI is empty, the generated target path copies the basic URI as it is Under these conditions, the "merge" logic is not executed Contrary to this specification, Java's URI implementation trims the basic path after the last character, as described in the problem

If you want RFC 3986 behavior, you can use an alternative method of the URI class Java EE 6 implementation provides javax ws. rs.core. Uribuilder, (in Jersey 1.18) seems to behave as expected (see below) At least in terms of RFC encoding, it is important to encode different URI components

In addition to J2EE, spring 3.0 introduces uriutils, which specifically records "encoding and decoding based on RFC 3986" Spring 3.1 does not recommend some of these functions and introduces uricomponents builder, but it does not comply with any specific RFC. Unfortunately

Test the program to show different behaviors:

import java.net.*;
import java.util.*;
import java.util.function.*;
import javax.ws.rs.core.UriBuilder; // using Jersey 1.18

public class StackOverflow22203111 {

    private URI withResolveURI(URI base,String targetQuery) {
        URI reference = queryOnlyURI(targetQuery);
        return base.resolve(reference);
    }

    private URI withUriBuilderReplaceQuery(URI base,String targetQuery) {
        UriBuilder builder = UriBuilder.fromUri(base);
        return builder.replaceQuery(targetQuery).build();
    }

    private URI withUriBuilderMergeURI(URI base,String targetQuery) {
        URI reference = queryOnlyURI(targetQuery);
        UriBuilder builder = UriBuilder.fromUri(base);
        return builder.uri(reference).build();
    }

    public static void main(String... args) throws Exception {

        final URI base = new URI("http://example.com/something/more/long");
        final String queryString = "query=http://local:282/rand&action=aaaa";
        final String expected =
            "http://example.com/something/more/long?query=http://local:282/rand&action=aaaa";

        StackOverflow22203111 test = new StackOverflow22203111();
        Map<String,BiFunction<URI,String,URI>> strategies = new LinkedHashMap<>();
        strategies.put("URI.resolve(URI)",test::withResolveURI);
        strategies.put("UriBuilder.replaceQuery(String)",test::withUriBuilderReplaceQuery);
        strategies.put("UriBuilder.uri(URI)",test::withUriBuilderMergeURI);

        strategies.forEach((name,method) -> {
            System.out.println(name);
            URI result = method.apply(base,queryString);
            if (expected.equals(result.toString())) {
                System.out.println("   MATCHES: " + result);
            }
            else {
                System.out.println("  EXPECTED: " + expected);
                System.out.println("   but WAS: " + result);
            }
        });
    }

    private URI queryOnlyURI(String queryString)
    {
        try {
            String scheme = null;
            String authority = null;
            String path = null;
            String fragment = null;
            return new URI(scheme,authority,path,fragment);
        }
        catch (URISyntaxException SyntaxError) {
            throw new IllegalStateException("unexpected",SyntaxError);
        }
    }
}

Output:

URI.resolve(URI)
  EXPECTED: http://example.com/something/more/long?query=http://local:282/rand&action=aaaa
   but WAS: http://example.com/something/more/?query=http://local:282/rand&action=aaaa
UriBuilder.replaceQuery(String)
   MATCHES: http://example.com/something/more/long?query=http://local:282/rand&action=aaaa
UriBuilder.uri(URI)
   MATCHES: http://example.com/something/more/long?query=http://local:282/rand&action=aaaa
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>