Issues with Set-Cookie that doesn't work for some reason

2,349 views
Skip to first unread message

Eliezer Croitoru

unread,
Jan 25, 2016, 12:25:27 PM1/25/16
to golang-nuts
I am working with some http responses objects and for some reason the
browser doesn't accept the cookie.
I have tried to somehow make sense of it using the browser development
console but I didn't managed to find something that will hint me on the
issue.

I am adding a "Set-Cookie" header and firefox refuses to use it for some
reason.

This example doesn't work as expected and firefox is not using the cookie:
http://play.golang.org/p/uOZqpYxyDa

While crafting the it manually works as expected and firefox is using it:
http://play.golang.org/p/khrOmNejeB

The only thing I noticed is that the cookie.String() places the expires
in a different position and missing ";" at the end.

If anyone has some idea of why firefox doesn't accept the cookie it will
help me a lot.

Thanks,
Eliezer

Volker Dobler

unread,
Jan 25, 2016, 5:53:55 PM1/25/16
to golang-nuts, eli...@ngtech.co.il
Just some remarks (I have no idea what's wrong):

Use http.SetCookie instead of manually adding it to the header map.

There is no need to do a .UTC() on time.Now() as the timezone is fixed anyway.

Don't use Expires, but MaxAge.

If one version of your works, the other will work too given everything else
is really the same.

V.

Eliezer Croitoru

unread,
Jan 25, 2016, 8:30:01 PM1/25/16
to golang-nuts, msilv...@cloudflare.com
First thanks for the responses.
Comments inline.

On 26/01/2016 01:42, msilv...@cloudflare.com wrote:
> > The only thing I noticed is that the cookie.String() places the expires
> in a different position and missing ";" at the end.
>
> You don't need a semicolon (";") at the end if there's only one cookie
> in the Set-Cookie header.

I know that but it works for now and I might add later more cookies.

> As Volker points out though, use `http.Cookie(w, cookie)` to write
> cookies to a http.ResponseWriter.

On A web server I am using the http.Cookie but the issue is that I am
crafting the http.Response manually for both testing and as an
encapsulated entity. In the past when I started working with this format
and crafting the response myself I tried couple options and this is the
only one that actually gave me what I Need.

Also about the UTC thing.. I am using it since I found couple cases
which the result was wrong. I think it was when using some http format
then the time that is set is the local but the output shows GMT.

My next step will be to identify what if I change the cookie.String()
function to result with the same format I am using instead.

Wish me luck,
Eliezer


Eliezer Croitoru

unread,
Jan 25, 2016, 9:44:20 PM1/25/16
to golan...@googlegroups.com
OK so I found the issue!
The issue was not in the cookie header but the Date header.
It seems that there some bug in this function command:
time.Now().Format(http.TimeFormat)

The command will output the current local time with GMT so what happens
is that the cookie has the right time as 3:40 and the server Date would
be one hour after ie 4:40.
So using in the Date header:
time.Now().UTC().Format(http.TimeFormat)

fixes all up and shows the real server Date and there for the cookie
will be still valid.

Thanks,
Eliezer

* this is the second time this happens to me :\

msilv...@cloudflare.com

unread,
Jan 25, 2016, 10:51:46 PM1/25/16
to golang-nuts, eli...@ngtech.co.il
> The only thing I noticed is that the cookie.String() places the expires 
in a different position and missing ";" at the end. 

You don't need a semicolon (";") at the end if there's only one cookie in the Set-Cookie header.

As Volker points out though, use `http.Cookie(w, cookie)` to write cookies to a http.ResponseWriter.

Volker Dobler

unread,
Jan 26, 2016, 1:50:31 AM1/26/16
to golang-nuts, eli...@ngtech.co.il
Am Dienstag, 26. Januar 2016 03:44:20 UTC+1 schrieb Eliezer Croitoru:
OK so I found the issue!
The issue was not in the cookie header but the Date header.
It seems that there some bug in this function command:
time.Now().Format(http.TimeFormat)

The command will output the current local time with GMT so what happens
is that the cookie has the right time as 3:40 and the server Date would
be one hour after ie 4:40.
So using in the Date header:
time.Now().UTC().Format(http.TimeFormat)

That's why you should use MaxAge and not Expires.

V.

Eliezer Croitoru

unread,
Jan 26, 2016, 11:31:02 AM1/26/16
to golan...@googlegroups.com
On 26/01/2016 08:50, Volker Dobler wrote:
>
> That's why you should use MaxAge and not Expires.
>
> V.
No you don't...
If the basic "time.Now().Format(http.TimeFormat)" function does only
formatting and doesn't actually calculate the time for UTC\GMT it has an
issue... it's a bug..

Eliezer

msilv...@cloudflare.com

unread,
Jan 26, 2016, 11:49:15 AM1/26/16
to golang-nuts, eli...@ngtech.co.il
Time.Format (https://golang.org/pkg/time/#Time.Format) only applies the formatting to the given time: it does not change the time.

When passing a time to cookie.Expires, just pass a time.Time type (e.g. time.Now().AddTime(0, 1, 0)) - there's no need to manipulate strings yourself. http.SetCookie will format the Expires field (using GMT/http.TimeFormat) correctly.

e.g.

func CookieHandler(w http.ResponseWriter, r *http.Request) {
month := time.Now().AddDate(0, 1, 0)

cookie := &http.Cookie{
Name:    "_app_session",
Value:   "<some-string>",
Expires: month,
}

// Sets the current time as GMT (which would be wrong)
fmt.Fprintf(w, "%v\n%v\n", month.Format(http.TimeFormat), cookie)
// Writes the correct time to the Expires field, adjusting for GMT.
http.SetCookie(w, cookie)
}

If you can show why you can't use http.SetCookie (as mentioned to in an earlier response) we may be able to help resolve that. You can still use a custom http.ResponseWriter (which is just an interface type) with it for testing.

Eliezer Croitoru

unread,
Jan 26, 2016, 11:58:37 AM1/26/16
to msilv...@cloudflare.com, golang-nuts
Hey,

The issue is not in the cookie but the request Date.
I am crafting a response from 0 and I need more then just he
responseWritter interface while doing so.
The issue is that the http.TimeFormat is weird. Why would I want to
format a string with a GMT if the time is not GMT??
I have seen that everywhere in the source when this format is being used
it is used with X.UTC() to convert it to a real GMT\UTC time.

Eliezer


On 26/01/2016 18:48, msilv...@cloudflare.com wrote:
> Time.Format (https://golang.org/pkg/time/#Time.Format) only applies the
> /formatting/ to the given time: it does not change the time.

Matthew Silverlock

unread,
Jan 26, 2016, 12:01:33 PM1/26/16
to Eliezer Croitoru, golang-nuts
You are correct that Time.UTC() will convert a time to UTC.

But Time.Format(someFormatConstant) only changes the formatting. This is often used where you want to change an existing, valid time from one format to the next (RFC3339 to RFC1123, for example). 

The documentation could possibly be clearer here but I wouldn't count the behavior as a bug.
--
Matt Silverlock
CloudFlare, Inc.
o +1.650.319.8930 (ext. 1)
e msilv...@cloudflare.com

Volker Dobler

unread,
Jan 26, 2016, 12:18:28 PM1/26/16
to golang-nuts, eli...@ngtech.co.il
Please excuse if my explanation was misleading.

As you know there are two ways to specify the lifetime of a persistent cookie:
MaxAge and Expires.  Expires is nasty and sometimes unreliable (think of
server and client disagreeing on the current time). To overcome these
nastiness MaxAge was introduced and is understood by every browser.
MaxAge is much easier to use and free form several problems Expires has.
That's why I'd like to recommend MaxAge, regardless of any potential
bugs with time formating.

Regarding your objection to time.Format and the timezone and how this
all interplays when crafting an Expires attribute of a cookie:
1. Note that a Go time.Time carries a location, a timezone.
2. As others have pointed out already Time.Format just does printing,
    and does not do any conversions, not even timezone conversions.
3. One of the nastinesses of Expires is that it _has_ to be in GMT, no
   arguing here, that is what the RFCs for HTTP define.
So you have to convert your time to UTC (=GMT) and then print it.
There is no bug, it is just the case that Expires is complicated.

That's why I recommended using MaxAge.

V.






Eliezer Croitoru

unread,
Jan 26, 2016, 12:59:00 PM1/26/16
to Volker Dobler, golang-nuts
On 26/01/2016 19:18, Volker Dobler wrote:
> Regarding your objection to time.Format and the timezone and how this
> all interplays when crafting an Expires attribute of a cookie:
> 1. Note that a Go time.Time carries a location, a timezone.
> 2. As others have pointed out already Time.Format just does printing,
> and does not do any conversions, not even timezone conversions.
> 3. One of the nastinesses of Expires is that it _has_ to be in GMT, no
> arguing here, that is what the RFCs for HTTP define.
> So you have to convert your time to UTC (=GMT) and then print it.
> There is no bug, it is just the case that Expires is complicated.
>
> That's why I recommended using MaxAge.
>
> V.

Thanks for the suggestions.
I understand it's not exactly a coding bug but it's not mentioned in the
docs of the http.TimeFormat.

I also understand the issue with Expires but eventually it seems that
browsers agree that the Expires in the response is relative to the
server. The server describe to the client the specifications and the
client is deciding how to enforce and implement it.

For now I am happy I found the issue since I had really hard time
finding the issue.

Thanks,
Eliezer
Reply all
Reply to author
Forward
0 new messages