Subject: Re: [http-state] nameless valueless cookies, and http vs javascript

From: Dan Witte <[email-masked]>
Date: Mon, 14 Dec 2009 14:29:09 -0800 (PST)

The reason Firefox differs in HTTP and JS behavior is because necko (our networking engine) strips out empty incoming HTTP headers before processing them. (See http://mxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp#57.) With JS, however, the content engine calls directly into cookie APIs and the empty string is processed. I'm not sure if the header stripping behavior is part of the HTTP spec (certainly coalescing is, which happens a few lines later in that function, and it's noteworthy that we coalesce Set-Cookie headers using newlines rather than the usual comma).

As Dan Winship points out, the whole point of allowing nameless cookies in Firefox was to fix a top 100 site at the time. I would guess they're still used today, but they're not such a great idea as demonstrated by Firefox and Chrome behavior. I'd speculate that the reason we haven't seen reports of nameless HTTP cookie breakage is because webserver frameworks probably don't generate them, whereas client JS code is much more prone to exercising weird corner cases. I'd say anything we can do to discourage this behavior and require both a non-empty name and (possibly empty) value to be present would be a good thing; but that's a discussion for another time!

Regards,
Dan.

----- Original Message -----
From: "Adam Barth" <[email-masked]>
To: "Dan Winship" <[email-masked]>
Cc: "http-state" <[email-masked]>, "Dan Witte" <[email-masked]>
Sent: Saturday, December 5, 2009 6:10:00 PM
Subject: Re: [http-state] nameless valueless cookies, and http vs javascript

These are great cases.  Thanks.  (dwitte, there's a question for you
at the bottom.)

On Tue, Dec 1, 2009 at 1:59 PM, Dan Winship <[email-masked]> wrote:
> The spec allows for a cookie in Set-Cookie to have both a 0-length name
> and a 0-length value (which can be expressed as either "Set-Cookie: " or
> "Set-Cookie: ="). Later it says that:
>
>   the user agent SHOULD attach exactly one HTTP header named Cookie if
>   the cookie-string (defined below) for that URI is non-empty.
>
> But this is not quite right, because it would imply that if you had an
> "empty" cookie and also some non-empty cookies, that you *would* emit
> the empty cookie as well. Eg:
>
>    test:
>      Set-Cookie: a=b
>      Set-Cookie: =
>      Set-Cookie: c=d
>
>    expected:
>      Cookie: a=b; ; c=d
>
> but Firefox outputs "Cookie: a=b; c=d". Assuming other browsers behave
> the same, we need to add something like "either the cookie name or
> cookie value or both is non-empty" to the cookie-list requirements in
> 5.3 part 1. (And then perhaps change the "cookie-string is non-empty"
> rule to "cookie-list is non-empty".)

Here's how some browser behave:

Test Case 0020:

Set-Cookie: a=b
Set-Cookie: =
Set-Cookie: c=d

Internet Explorer, Firefox, Safari, Opera: a=b; c=d
Chrome: a=b; ; c=d

It seems clear we should do as you suggest and spec the
IE/Firefox/Safari/Opera behavior.  I've add two more tests (0021 and
0022) that show the cookie is elided only when it would be represented
by the empty string.

Given the considerations below, I've left the empty/empty cookie in
the model of the state store, but elided it during serialization.

> Another issue: we test that "Set-Cookie: =" and "Set-Cookie: " don't
> cause cookies to be emitted, but we don't test how they interact with
> previous Set-Cookies. The current rules suggest:
>
>    test:
>      Set-Cookie: foo
>      Set-Cookie:
>
>    expected:
>      (empty)

Now test 0023

> and
>    test:
>      Set-Cookie: foo
>      Set-Cookie: =
>    expected:
>      (empty)

Now test 0024

> Firefox gives the expected answer on the second one, but gives "Cookie:
> foo" on the first, suggesting that it just completely ignores the blank
> Set-Cookie header.

Here's the compatibility matrix:

0023:
(empty): Internet Explorer, Safari,
foo: Firefox, Chrome

0024:
(empty): Internet Explorer, Firefox, Safari, Chrome

> However, if you do it in JavaScript, you get the
> expected answer either way:
>
>      >>> document.cookie="foo"
>      >>> document.cookie
>      "foo"
>      >>> document.cookie=""
>      >>> document.cookie
>      ""
>
> The fact that this works this way in javascript is intentional
> (https://bugzilla.mozilla.org/show_bug.cgi?id=169091#c16). I'm not sure
> if the different behavior via HTTP is intentional or not.

Interesting.  It's clear we have the right behavior for 0024 because
everyone agrees.  I don't have a strong opinion about what to do with
0023.

dwitte, what do you think?

Adam

---

From: Adam Barth <[email-masked]>
Date: Mon, 14 Dec 2009 14:41:33 -0800

> As Dan Winship points out, the whole point of allowing nameless cookies in Firefox was to fix a top 100 site at the time. I would guess they're still used today, but they're not such a great idea a…

The server conformance section forbids servers from using nameless
cookies.  Hopefully that will discourage their use.

Adam

---

From: "Roy T. Fielding" <[email-masked]>
Date: Mon, 14 Dec 2009 15:05:13 -0800

> The reason Firefox differs in HTTP and JS behavior is because necko (our networking engine) strips out empty incoming HTTP headers before processing them. (See http://mxr.mozilla.org/mozilla-centra…

That stripping (as a general case) would be a violation of
the HTTP spec.  Note the difference between

   Accept:
and
   Accept: */*

The first says that I don't want server-driven negotiation.

Off-hand, I don't think there is a deployed response header
that depends on the meaning on an empty field, but there
certainly isn't any reason to exclude empty fields as if
they weren't communicated at all.

Set-Cookie should not be allowed to be empty.  The grammar
for a header field defines what is allowed to be generated
by a compliant sender, not how a recipient may parse it
for robustness.  An empty Set-Cookie (the specific case)
can therefore be stripped during processing without changing
the meaning of the message.

....Roy

---

From: Adam Barth <[email-masked]>
Date: Mon, 14 Dec 2009 15:18:20 -0800

On Mon, Dec 14, 2009 at 3:05 PM, Roy T. Fielding <[email-masked]> wrote:
> for robustness.

Indeed.  The spec forbids servers from sending an empty Set-Cookie header.

> An empty Set-Cookie (the specific case)

The question is how user agents ought to behave when they receive such
a forbidden header.  In the end, they have to do something (even if
that something is to ignore the header).  As we see from test 0023,
different user agents do different things.  It appears that Internet
Explorer and Safari delete the nameless cookie whereas Firefox and
Chrome ignore the header.

Adam