W3C home > Mailing lists > Public > public-webapps@w3.org > January to March 2009

Re: [cors] Redirects and preflights

From: Jonas Sicking <jonas@sicking.cc>
Date: Thu, 5 Mar 2009 01:18:39 -0800
Message-ID: <63df84f0903050118j2119aa91y2ec0bdf2c8b1bcd4@mail.gmail.com>
To: Anne van Kesteren <annevk@opera.com>
Cc: Webapps WG <public-webapps@w3.org>
>> What about the following scenario:
>> 1. A page on site A initiates a DELETE request to a uri on site B
>> 2. The UA makes a preflight OPTIONS request to the uri on site B
>> 3. The site responds and says the original DELETE request is ok
>> 4. The UA makes the DELETE request to site B
>> 5. The site responds with a 303 that redirects to a different uri on site
>> B.
>
> Network error steps.
>
>
>> Should this redirect not be followed even though it technically does
>> not require a preflight?
>
> Correct. All redirects on the actual request fail.

I don't think that is desirable nor necessary, see below.

>> I think it's fairly important that we do follow the redirect since a
>> common way to deal with state changing requests is to redirect to a
>> URI that contains the response. This avoids ugly "you have to refresh
>> to display this page" pages in browsers. So I think the redirect
>> should be followed.
>
> Why would there be no redirect on the OPTIONS request?

It's very possible for a server to serve different redirects for
different request methods. My understanding of the HTTP spec is that a
303 response is meant to mean that the current uri has handled the
request, but that the response is located at a different uri.

For normal browser navigation this is the recommended way to, for
example, allow a POST to a shopping cart that adds an item to the
shopping cart, while still allowing the user to navigate back to the
result page without worrying about another item being added to the
shopping cart. A POST is performed to a uri that adds the desired
item, but then uses a 303 response to redirect the user to the page
that actually displays the shopping cart. If the user then navigates
away from that page, and then back to it, it'll still result in a safe
GET request to the uri that displays the shopping cart, without adding
any additional items to it.

So this is quite a common pattern on the web today, and ideally should
be more common. Rather than using the state-changing GET requests
sites do today to avoid having to deal with the ugly "you need to
refresh to display this page again" error pages that browsers produce
from un-redirected POST requests.

For this setup it makes a lot of sense to not redirect a OPTIONS
request, but to redirect a POST request to the same uri.


>> What should be done in the following scenario:
>> 1. A page on site A initiates a DELETE request to a uri on site B
>> 2. The UA makes a preflight OPTIONS request to the uri on site B
>> 3. The site responds with a 303 to a different uri on site B.
>
> Currently the specification treats all redirects the same. I guess we could
> special case 303.

In the setup described above, even if the server redirects the
add-item-to-cart OPTIONS response to the display-shopping-cart uri, it
makes little sense to redirect the POST there. That would never have
happened for a same-site POST request going to the add-item-to-cart
uri.

>> Should perhaps
>> a 303 redirect mean that the DELETE request should be made to the
>> initial uri on site B, assuming that the preflight after redirects
>> ended up returning a response with the appropriate headers?
>
> No, that seems wrong.

Why? A 303 just means that the response is located elsewhere, not that
the action is performed by a different uri.

>> I don't have strong opinions on this one. But a gut feeling is that
>> only a 307 should cause the DELETE to go somewhere else as that is the
>> only time when that would have happened if the DELETE would have been
>> done directly to the first uri on site B.
>
> 301 and 302 should do that too.

Why? Note that 302 is basically treated as a 303 by most browsers
today just because what the spec says is basically terrible UI asking
the user to make a decision he/she does not have enough information to
make.

More below.

>> And here's another:
>> 1. A page on site A initiates a POST request to a uri on site B. The
>> request is
>>   a entity body and a Content-Type of "text/plain"
>> 2. Since no preflight is needed The UA makes the request to the uri on
>> site B
>> 3. Before the server has responded, the page adds a event listener for the
>>   progress event on the upload object. (This listener won't be
>> notified according
>>   to spec)
>> 4. The server responds with a 307 redirect to another URI on site B.
>>
>> Should the request to the new URI on site be now be done with a
>> preflight? Or should the algorithm abort because a preflight is now
>> required but there is a redirect. Or should the redirect be followed
>> but the event listener not be notified? In other words, should the
>> fact that there is upload event listeners be measured on just the
>> initial request, or on each redirect.
>>
>> I think the fact that there is upload event listeners should be
>> measured just on the initial request. So in the example above the
>> redirect should be followed but no events would be fired on the upload
>> object.
>
> I think this is already clear in the specification.

My understanding is that the answer is that it's only measured in the
beginning. Is this correct?


>> The following scenario might be gecko-specific:
>> 1. A page on site A initiates a POST request to a uri on site B. The
>> request is
>>   not given a body, but is given a Content-Type of "text/plain"
>> 2. Since no preflight is needed The UA makes the request to the uri on
>> site B
>> 3. The server responds with a 307 redirect to another URI on site B.
>>
>> In this case Gecko creates a new request that doesn't have a
>> Content-Type header since there is no body. I suspect this is Gecko
>> specific behavior though and so nothing that needs to be addressed
>> specifically. Would be interesting to hear if other browsers have the
>> same behavior though.
>
> That seems like a bug.

I will investigate further.


>> So to sum things up. I suggest the following behavior:
>> * When the actual request (i.e. not a preflight request) is done, only
>> abort with a network error if the newly created request would require
>> a preflight. So don't abort in the case when the redirect changes the
>> request from one that requires a preflight into one that doesn't.
>> (This means that in the gecko-specific case above, abort with a
>> network error since the newly createde request does require
>> preflight).
>
> This case seems so marginal that it does not seem worth the complexity to
> me. It only ever occurs in case of 303 where the preflight request is not
> redirected for whatever reason.

As described above, it is in fact the recommended way to allow the UA
to fetch the response to the request multiple times without causing
the action to happen every time. So there are several servers out
there that behave this way already, and once we add support for
redirects to XHR, it's most likely something that developers will want
to do in XHR as well.


>> * When making the OPTIONS preflight request, only change the
>> destination uri of the actual request if the response is a 307.
>
> Why not for 301 and 302?

In my examples I've used 303 and 307 as they are the most unambigios
redirections. 302 is generally treated as 303, though the spec allows
it to be treated as a 307 after asking for user permission. I'm
unclear on how 301s are supposed to work, but at least in gecko they
do seem to behave like a 303, though with some effect on following
requests that I haven't yet dug in to (which is what sets them, as
permanent redirects, apart from temporary redirects).


In short, I think the spec currently misunderstands the HTTP protocol
by treating all redirects as redirects of the request, whereas many of
them are just redirects of the response.

/ Jonas
Received on Thursday, 5 March 2009 09:19:19 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 18:49:30 GMT