Subject: Re: #295: Applying original fragment to "plain" redirected URI (also #43)

From: Julian Reschke <[email-masked]>
Date: Tue, 03 Jan 2012 15:43:37 +0100

On 2011-12-30 18:51, Julian Reschke wrote:
> ...
> Indeed; see my tests at
> <http://greenbytes.de/tech/tc/httpredirects/#l-fragments> (note that
> Safari appears to have funny issues filling the iframes; but navigating
> to the linked resource gets you proper results).
> ...

I just realized that the rule we would need to describe *almost* is the 
one define in the URI spec 
(<http://greenbytes.de/tech/webdav/rfc3986.html#rfc.section.5.2>) as 
"relative resolution":

>    -- The URI reference is parsed into the five URI components
>    --
>    (R.scheme, R.authority, R.path, R.query, R.fragment) = parse(R);
>
>    -- A non-strict parser may ignore a scheme in the reference
>    -- if it is identical to the base URI's scheme.
>    --
>    if ((not strict) and (R.scheme == Base.scheme)) then
>       undefine(R.scheme);
>    endif;
>
>    if defined(R.scheme) then
>       T.scheme    = R.scheme;
>       T.authority = R.authority;
>       T.path      = remove_dot_segments(R.path);
>       T.query     = R.query;
>    else
>       if defined(R.authority) then
>          T.authority = R.authority;
>          T.path      = remove_dot_segments(R.path);
>          T.query     = R.query;
>       else
>          if (R.path == "") then
>             T.path = Base.path;
>             if defined(R.query) then
>                T.query = R.query;
>             else
>                T.query = Base.query;
>             endif;
>          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;
>          T.authority = Base.authority;
>       endif;
>       T.scheme = Base.scheme;
>    endif;
>
>    T.fragment = R.fragment;

"Almost", because it doesn't use Base.fragment when R.frament is undefined.

a) Should we try describe the algorithm based on RFC 3986 ("do relative 
resolution as defined by ..., then, if the result doesn't have a 
fragment, add the one from the Base URI")?

b) Is this potentially an erratum for RFC 3986?

Best regards, Julian

---

From: Larry Masinter <[email-masked]>
Date: Tue, 3 Jan 2012 20:57:01 -0800

...

a) Should we try describe the algorithm based on RFC 3986 ("do relative resolution as defined by ..., then, if the result doesn't have a fragment, add the one from the Base URI")?
b) Is this potentially an erratum for RFC 3986?

a) sounds good.
b) I'd call it an "update" rather than an erratum.

---

From: Amos Jeffries <[email-masked]>
Date: Wed, 04 Jan 2012 18:12:06 +1300

On 4/01/2012 3:43 a.m., Julian Reschke wrote:
> a) Should we try describe the algorithm based on RFC 3986 ("do 
> relative resolution as defined by ..., then, if the result doesn't 
> have a fragment, add the one from the Base URI")?

+1 on this.

> b) Is this potentially an erratum for RFC 3986?

um.

AYJ

---

From: Willy Tarreau <[email-masked]>
Date: Wed, 4 Jan 2012 06:45:13 +0100

> >have a fragment, add the one from the Base URI")?

+1 too.

Willy

---

From: "Martin J. Dürst" <[email-masked]>
Date: Wed, 04 Jan 2012 19:27:52 +0900

Hello Julian, others,

> fragment, add the one from the Base URI")?

I'm not at all sure that this description is correct. It would mean that 
I can have something like:
Request URI:   http://1.example.org/path1/file1.ext
Redirect URI:  http://2.example.org#frag2

and the result would be:
http://2.example.org/path1/file1.ext#frag2

As you can see in the result, there is a mixture of components from the 
request URI (1) and the redirect URI (2). The way that relative 
resolution works otherwise is that in the result, all components from 
(2) precede components from (1).

Below is the change to the algorithm that I'd think is correct. In 
logical terms, it's straightforward: Use the fragment from the "base" 
only if nothing before the fragment is coming from the "resource". 
However, in terms of actual code, there are quite a few places to 
change. This is because the if/else hierarchy gets deeper and deeper for 
the later parts of the URI. In the algorithm, scheme is set in two 
locations, authority in three, and so on. The structure of the code gets 
even more regular if you change
    if (R.path == "") then
to
    if (R.path != "") then
(which is equivalent to "if defined(R.path) then") and exchange the 
respective code blocks. The only irregularity in the structure then is the
    if (R.path starts-with "/") then
condition; this could be regularized by separating path (without the 
actual final name of the resource) and pure resource (file) name.

    if defined(R.scheme) then
       T.scheme    = R.scheme;
       T.authority = R.authority;
       T.path      = remove_dot_segments(R.path);
       T.query     = R.query;
       T.fragment  = R.fragment;               -- this line added
    else
       if defined(R.authority) then
          T.authority = R.authority;
          T.path      = remove_dot_segments(R.path);
          T.query     = R.query;
          T.fragment  = R.fragment;            -- this line added
       else
          if (R.path == "") then
             T.path = Base.path;
             if defined(R.query) then
                T.query = R.query;
                T.fragment = R.fragment;       -- this line added
             else
                T.query = Base.query;
                if defined(R.fragment) then    -- this line added
                   T.fragment  = R.fragment;   -- this line added
                else                           -- this line added
                   T.fragment = Base.fragment; -- this line added
                endif;                         -- this line added
             endif;
          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;
             T.fragment = R.fragment;          -- this line added
          endif;
          T.authority = Base.authority;
       endif;
       T.scheme = Base.scheme;
    endif;

    -- T.fragment = R.fragment;                -- this line commented out

It's also possible to rewrite this as:

    -- The URI reference is parsed into the five URI components
    --
    (R.scheme, R.authority, R.path, R.query, R.fragment) = parse(R);
    T.fragment = undefined;                    -- this line added

    if defined(R.scheme) then
       T.scheme    = R.scheme;
       T.authority = R.authority;
       T.path      = remove_dot_segments(R.path);
       T.query     = R.query;
    else
       if defined(R.authority) then
          T.authority = R.authority;
          T.path      = remove_dot_segments(R.path);
          T.query     = R.query;
       else
          if (R.path == "") then
             T.path = Base.path;
             if defined(R.query) then
                T.query = R.query;
             else
                T.query = Base.query;
                if not defined(R.fragment) then  -- this line added
                   T.fragment = Base.fragment;   -- this line added
                endif;                           -- this line added
             endif;
          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;
          T.authority = Base.authority;
       endif;
       T.scheme = Base.scheme;
    endif;

    if not defined(T.fragment) then            -- this line added
       T.fragment = R.fragment;
    endif;                                     -- this line added

This localizes the changes better and can probably serve as the base (no 
pun intended) for spec text.

> b) Is this potentially an erratum for RFC 3986?

I would say NO. My understanding is that something like
    <a href="">a link</a>
always refers to the resource itself, not a subresource. If the erratum 
went through, there would be no short way to refer to a resource itself.

Regards,    Martin.

---

From: Julian Reschke <[email-masked]>
Date: Wed, 04 Jan 2012 11:42:16 +0100

> http://2.example.org/path1/file1.ext#frag2

I think that's incorrect.

This should be resolved as per:

       (left out)
    endif;

    T.fragment = R.fragment;

thus to

    http://2.example.org#frag2

Best regards, Julian

---

From: Julian Reschke <[email-masked]>
Date: Thu, 05 Jan 2012 21:31:39 +0100

> b) I'd call it an "update" rather than an erratum.

OK, here's an attempt to describe this:

Appendix A.  Base Fragment Aware Relative Resolution

    Section 5.2 of [RFC3986] defines the Relative Resolution of a URI
    reference against a Base URI.  That algorithm however does not take a
    fragment identifier on the Base URI into account.

    However in some cases, as when following a redirect from a URI "Base"
    based on a Location header field containing the URI reference "R", it
    can be necessary to preserve a fragment identifier present on "Base".
    The algorithm described below does this:

    Given a URI reference "R" and a base URI "Base", to transform R into
    its target URI "T":

    (1) Pre-parse the Base URI as defined in Section 5.2.1 of [RFC3986].

    (2) Transform references as defined in Section 5.2.2 of [RFC3986].
    If the T.fragment is undefined and Base.fragment is defined, then set
    T.fragment to Base.fragment:

      if defined(Base.fragment) and not(defined(T.fragment)) then
        T.fragment = Base.fragment;
      endif;

    (3) Merge paths as defined in Section 5.2.3 of [RFC3986].

    (4) Remove dot segments as defined in Section 5.2.4 of [RFC3986].

    (5) Finally, recompose the components as defined in Section 5.3 of
    [RFC3986].

A.1.  Examples

    Starting with a Base URI of "http://host/path1#f1":

    +-----------+------------------------+------------------------------+
    | R         | T (after RFC 3986      | T (after Base Fragment Aware |
    |           | Relative Resolution)   | Relative Resolution)         |
    +-----------+------------------------+------------------------------+
    | /path2    | http://host/path2      | http://host/path2#f1         |
    | /path2#f2 | http://host/path2#f2   | http://host/path2#f2         |
    +-----------+------------------------+------------------------------+

(this could be an HTTPbis appendix or a standalone document if it's 
needed elsewhere)

Best regards, Julian

---

From: "Roy T. Fielding" <[email-masked]>
Date: Thu, 5 Jan 2012 13:48:22 -0800

Sorry, I'm not following this logic at all.  Reusing the original fragment
after a redirect is, at best, fallback behavior that would occur *inside*
the user agent renderer after URI parsing is done, after the redirect is
done, and without any reference to the Location URI.  In short, this whole
approach is wrong (and confusing).

....Roy

---

From: Julian Reschke <[email-masked]>
Date: Sat, 07 Jan 2012 16:28:30 +0100

> approach is wrong (and confusing).

OK, I think I see what you mean.

Going back to what we currently have in 
<http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p2-semantics-18.html#rfc.section.9.5.p.3>:

"The field value consists of a single URI-reference. When it has the 
form of a relative reference ([RFC3986], Section 4.2), the final value 
is computed by resolving it against the effective request URI 
([RFC3986], Section 5)."

and in 
<http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p2-semantics-18.html#rfc.section.9.5.p.9>:

"Note: This specification does not define precedence rules for the case 
where the original URI, as navigated to by the user agent, and the 
Location header field value both contain fragment identifiers. Thus be 
aware that including fragment identifiers might inconvenience anyone 
relying on the semantics of the original URI's fragment identifier."

(which was the result of negotiating the text with the W3C TAG, see 
issue #43).

The note we added back then already mentions fragment identifiers on the 
"original URI".

We need to decide whether to re-open #43 (fragment recombination), and 
what to do #295.

The three interesting cases are:

(1) <http://greenbytes.de/tech/tc/httpredirects/#tfnry>: 
http://example.com/test1 redirect-to http://example.com/test2#A yields ?

(2) <http://greenbytes.de/tech/tc/httpredirects/#tfyry>: 
http://example.com/test1#B redirect-to http://example.com/test2#A yields ?

(3) <http://greenbytes.de/tech/tc/httpredirects/#tfyrn>: 
http://example.com/test1#B redirect-to http://example.com/test2 yields ?

Browsers agree on the first two cases (the fragment from the redirect is 
used, so http://example.com/test2#A), but do not completely agree on the 
last case (Safari and IE loose the fragment identifier, the others keep 
it, ending up with http://example.com/test2#B).

I think we have heard from IE that they want to change to the 
Firefox/Chrome/Opera behavior. We don't know about Safari.

To make this change we could add to:

"The field value consists of a single URI-reference. When it has the 
form of a relative reference ([RFC3986], Section 4.2), the final value 
is computed by resolving it against the effective request URI 
([RFC3986], Section 5)."

saying

"... If the original URI, as navigated to by the user agent, did contain 
a fragment identifier, and the final value does not, then the original 
URI's fragment identifier is added to the final value."

(and also we would kill 
<http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p2-semantics-18.html#rfc.section.9.5.p.9>).

Best regards, Julian

---

From: Mark Nottingham <[email-masked]>
Date: Wed, 1 Feb 2012 16:13:54 +1100

On 08/01/2012, at 2:28 AM, Julian Reschke wrote:

> "The field value consists of a single URI-reference. When it has the form of a relative reference ([RFC3986], Section 4.2), the final value is computed by resolving it against the effective request URI ([RFC3986], Section 5)."
> 
> saying
> 
> "... If the original URI, as navigated to by the user agent, did contain a fragment identifier, and the final value does not, then the original URI's fragment identifier is added to the final value."
> 
> 
> (and also we would kill <http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p2-semantics-18.html#rfc.section.9.5.p.9>).

Works for me; +1. Some examples wouldn't go astray.

--
Mark Nottingham   http://www.mnot.net/