f78f37299f4885ed0407a74fa131c32b3cacc646
[platform/upstream/gcc.git] / libgo / go / net / http / cookie_test.go
1 // Copyright 2010 The Go Authors.  All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package http
6
7 import (
8         "bytes"
9         "encoding/json"
10         "fmt"
11         "log"
12         "os"
13         "reflect"
14         "strings"
15         "testing"
16         "time"
17 )
18
19 var writeSetCookiesTests = []struct {
20         Cookie *Cookie
21         Raw    string
22 }{
23         {
24                 &Cookie{Name: "cookie-1", Value: "v$1"},
25                 "cookie-1=v$1",
26         },
27         {
28                 &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
29                 "cookie-2=two; Max-Age=3600",
30         },
31         {
32                 &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
33                 "cookie-3=three; Domain=example.com",
34         },
35         {
36                 &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
37                 "cookie-4=four; Path=/restricted/",
38         },
39         {
40                 &Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
41                 "cookie-5=five",
42         },
43         {
44                 &Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
45                 "cookie-6=six",
46         },
47         {
48                 &Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
49                 "cookie-7=seven; Domain=127.0.0.1",
50         },
51         {
52                 &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
53                 "cookie-8=eight",
54         },
55         // The "special" cookies have values containing commas or spaces which
56         // are disallowed by RFC 6265 but are common in the wild.
57         {
58                 &Cookie{Name: "special-1", Value: "a z"},
59                 `special-1=a z`,
60         },
61         {
62                 &Cookie{Name: "special-2", Value: " z"},
63                 `special-2=" z"`,
64         },
65         {
66                 &Cookie{Name: "special-3", Value: "a "},
67                 `special-3="a "`,
68         },
69         {
70                 &Cookie{Name: "special-4", Value: " "},
71                 `special-4=" "`,
72         },
73         {
74                 &Cookie{Name: "special-5", Value: "a,z"},
75                 `special-5=a,z`,
76         },
77         {
78                 &Cookie{Name: "special-6", Value: ",z"},
79                 `special-6=",z"`,
80         },
81         {
82                 &Cookie{Name: "special-7", Value: "a,"},
83                 `special-7="a,"`,
84         },
85         {
86                 &Cookie{Name: "special-8", Value: ","},
87                 `special-8=","`,
88         },
89         {
90                 &Cookie{Name: "empty-value", Value: ""},
91                 `empty-value=`,
92         },
93 }
94
95 func TestWriteSetCookies(t *testing.T) {
96         defer log.SetOutput(os.Stderr)
97         var logbuf bytes.Buffer
98         log.SetOutput(&logbuf)
99
100         for i, tt := range writeSetCookiesTests {
101                 if g, e := tt.Cookie.String(), tt.Raw; g != e {
102                         t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g)
103                         continue
104                 }
105         }
106
107         if got, sub := logbuf.String(), "dropping domain attribute"; !strings.Contains(got, sub) {
108                 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
109         }
110 }
111
112 type headerOnlyResponseWriter Header
113
114 func (ho headerOnlyResponseWriter) Header() Header {
115         return Header(ho)
116 }
117
118 func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
119         panic("NOIMPL")
120 }
121
122 func (ho headerOnlyResponseWriter) WriteHeader(int) {
123         panic("NOIMPL")
124 }
125
126 func TestSetCookie(t *testing.T) {
127         m := make(Header)
128         SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
129         SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
130         if l := len(m["Set-Cookie"]); l != 2 {
131                 t.Fatalf("expected %d cookies, got %d", 2, l)
132         }
133         if g, e := m["Set-Cookie"][0], "cookie-1=one; Path=/restricted/"; g != e {
134                 t.Errorf("cookie #1: want %q, got %q", e, g)
135         }
136         if g, e := m["Set-Cookie"][1], "cookie-2=two; Max-Age=3600"; g != e {
137                 t.Errorf("cookie #2: want %q, got %q", e, g)
138         }
139 }
140
141 var addCookieTests = []struct {
142         Cookies []*Cookie
143         Raw     string
144 }{
145         {
146                 []*Cookie{},
147                 "",
148         },
149         {
150                 []*Cookie{{Name: "cookie-1", Value: "v$1"}},
151                 "cookie-1=v$1",
152         },
153         {
154                 []*Cookie{
155                         {Name: "cookie-1", Value: "v$1"},
156                         {Name: "cookie-2", Value: "v$2"},
157                         {Name: "cookie-3", Value: "v$3"},
158                 },
159                 "cookie-1=v$1; cookie-2=v$2; cookie-3=v$3",
160         },
161 }
162
163 func TestAddCookie(t *testing.T) {
164         for i, tt := range addCookieTests {
165                 req, _ := NewRequest("GET", "http://example.com/", nil)
166                 for _, c := range tt.Cookies {
167                         req.AddCookie(c)
168                 }
169                 if g := req.Header.Get("Cookie"); g != tt.Raw {
170                         t.Errorf("Test %d:\nwant: %s\n got: %s\n", i, tt.Raw, g)
171                         continue
172                 }
173         }
174 }
175
176 var readSetCookiesTests = []struct {
177         Header  Header
178         Cookies []*Cookie
179 }{
180         {
181                 Header{"Set-Cookie": {"Cookie-1=v$1"}},
182                 []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
183         },
184         {
185                 Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
186                 []*Cookie{{
187                         Name:       "NID",
188                         Value:      "99=YsDT5i3E-CXax-",
189                         Path:       "/",
190                         Domain:     ".google.ch",
191                         HttpOnly:   true,
192                         Expires:    time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
193                         RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
194                         Raw:        "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
195                 }},
196         },
197         {
198                 Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
199                 []*Cookie{{
200                         Name:       ".ASPXAUTH",
201                         Value:      "7E3AA",
202                         Path:       "/",
203                         Expires:    time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
204                         RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
205                         HttpOnly:   true,
206                         Raw:        ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
207                 }},
208         },
209         {
210                 Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
211                 []*Cookie{{
212                         Name:     "ASP.NET_SessionId",
213                         Value:    "foo",
214                         Path:     "/",
215                         HttpOnly: true,
216                         Raw:      "ASP.NET_SessionId=foo; path=/; HttpOnly",
217                 }},
218         },
219         // Make sure we can properly read back the Set-Cookie headers we create
220         // for values containing spaces or commas:
221         {
222                 Header{"Set-Cookie": {`special-1=a z`}},
223                 []*Cookie{{Name: "special-1", Value: "a z", Raw: `special-1=a z`}},
224         },
225         {
226                 Header{"Set-Cookie": {`special-2=" z"`}},
227                 []*Cookie{{Name: "special-2", Value: " z", Raw: `special-2=" z"`}},
228         },
229         {
230                 Header{"Set-Cookie": {`special-3="a "`}},
231                 []*Cookie{{Name: "special-3", Value: "a ", Raw: `special-3="a "`}},
232         },
233         {
234                 Header{"Set-Cookie": {`special-4=" "`}},
235                 []*Cookie{{Name: "special-4", Value: " ", Raw: `special-4=" "`}},
236         },
237         {
238                 Header{"Set-Cookie": {`special-5=a,z`}},
239                 []*Cookie{{Name: "special-5", Value: "a,z", Raw: `special-5=a,z`}},
240         },
241         {
242                 Header{"Set-Cookie": {`special-6=",z"`}},
243                 []*Cookie{{Name: "special-6", Value: ",z", Raw: `special-6=",z"`}},
244         },
245         {
246                 Header{"Set-Cookie": {`special-7=a,`}},
247                 []*Cookie{{Name: "special-7", Value: "a,", Raw: `special-7=a,`}},
248         },
249         {
250                 Header{"Set-Cookie": {`special-8=","`}},
251                 []*Cookie{{Name: "special-8", Value: ",", Raw: `special-8=","`}},
252         },
253
254         // TODO(bradfitz): users have reported seeing this in the
255         // wild, but do browsers handle it? RFC 6265 just says "don't
256         // do that" (section 3) and then never mentions header folding
257         // again.
258         // Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
259 }
260
261 func toJSON(v interface{}) string {
262         b, err := json.Marshal(v)
263         if err != nil {
264                 return fmt.Sprintf("%#v", v)
265         }
266         return string(b)
267 }
268
269 func TestReadSetCookies(t *testing.T) {
270         for i, tt := range readSetCookiesTests {
271                 for n := 0; n < 2; n++ { // to verify readSetCookies doesn't mutate its input
272                         c := readSetCookies(tt.Header)
273                         if !reflect.DeepEqual(c, tt.Cookies) {
274                                 t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
275                                 continue
276                         }
277                 }
278         }
279 }
280
281 var readCookiesTests = []struct {
282         Header  Header
283         Filter  string
284         Cookies []*Cookie
285 }{
286         {
287                 Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
288                 "",
289                 []*Cookie{
290                         {Name: "Cookie-1", Value: "v$1"},
291                         {Name: "c2", Value: "v2"},
292                 },
293         },
294         {
295                 Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
296                 "c2",
297                 []*Cookie{
298                         {Name: "c2", Value: "v2"},
299                 },
300         },
301         {
302                 Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
303                 "",
304                 []*Cookie{
305                         {Name: "Cookie-1", Value: "v$1"},
306                         {Name: "c2", Value: "v2"},
307                 },
308         },
309         {
310                 Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
311                 "c2",
312                 []*Cookie{
313                         {Name: "c2", Value: "v2"},
314                 },
315         },
316 }
317
318 func TestReadCookies(t *testing.T) {
319         for i, tt := range readCookiesTests {
320                 for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
321                         c := readCookies(tt.Header, tt.Filter)
322                         if !reflect.DeepEqual(c, tt.Cookies) {
323                                 t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
324                                 continue
325                         }
326                 }
327         }
328 }
329
330 func TestCookieSanitizeValue(t *testing.T) {
331         defer log.SetOutput(os.Stderr)
332         var logbuf bytes.Buffer
333         log.SetOutput(&logbuf)
334
335         tests := []struct {
336                 in, want string
337         }{
338                 {"foo", "foo"},
339                 {"foo;bar", "foobar"},
340                 {"foo\\bar", "foobar"},
341                 {"foo\"bar", "foobar"},
342                 {"\x00\x7e\x7f\x80", "\x7e"},
343                 {`"withquotes"`, "withquotes"},
344                 {"a z", "a z"},
345                 {" z", `" z"`},
346                 {"a ", `"a "`},
347         }
348         for _, tt := range tests {
349                 if got := sanitizeCookieValue(tt.in); got != tt.want {
350                         t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want)
351                 }
352         }
353
354         if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
355                 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
356         }
357 }
358
359 func TestCookieSanitizePath(t *testing.T) {
360         defer log.SetOutput(os.Stderr)
361         var logbuf bytes.Buffer
362         log.SetOutput(&logbuf)
363
364         tests := []struct {
365                 in, want string
366         }{
367                 {"/path", "/path"},
368                 {"/path with space/", "/path with space/"},
369                 {"/just;no;semicolon\x00orstuff/", "/justnosemicolonorstuff/"},
370         }
371         for _, tt := range tests {
372                 if got := sanitizeCookiePath(tt.in); got != tt.want {
373                         t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want)
374                 }
375         }
376
377         if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
378                 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
379         }
380 }