Source file
src/net/http/cookie_test.go
1
2
3
4
5 package http
6
7 import (
8 "encoding/json"
9 "errors"
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 {
56 &Cookie{Name: "cookie-9", Value: "expiring", Expires: time.Unix(1257894000, 0)},
57 "cookie-9=expiring; Expires=Tue, 10 Nov 2009 23:00:00 GMT",
58 },
59
60 {
61 &Cookie{Name: "cookie-10", Value: "expiring-1601", Expires: time.Date(1601, 1, 1, 1, 1, 1, 1, time.UTC)},
62 "cookie-10=expiring-1601; Expires=Mon, 01 Jan 1601 01:01:01 GMT",
63 },
64 {
65 &Cookie{Name: "cookie-11", Value: "invalid-expiry", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)},
66 "cookie-11=invalid-expiry",
67 },
68 {
69 &Cookie{Name: "cookie-12", Value: "samesite-default", SameSite: SameSiteDefaultMode},
70 "cookie-12=samesite-default",
71 },
72 {
73 &Cookie{Name: "cookie-13", Value: "samesite-lax", SameSite: SameSiteLaxMode},
74 "cookie-13=samesite-lax; SameSite=Lax",
75 },
76 {
77 &Cookie{Name: "cookie-14", Value: "samesite-strict", SameSite: SameSiteStrictMode},
78 "cookie-14=samesite-strict; SameSite=Strict",
79 },
80 {
81 &Cookie{Name: "cookie-15", Value: "samesite-none", SameSite: SameSiteNoneMode},
82 "cookie-15=samesite-none; SameSite=None",
83 },
84 {
85 &Cookie{Name: "cookie-16", Value: "partitioned", SameSite: SameSiteNoneMode, Secure: true, Path: "/", Partitioned: true},
86 "cookie-16=partitioned; Path=/; Secure; SameSite=None; Partitioned",
87 },
88
89
90 {
91 &Cookie{Name: "special-1", Value: "a z"},
92 `special-1="a z"`,
93 },
94 {
95 &Cookie{Name: "special-2", Value: " z"},
96 `special-2=" z"`,
97 },
98 {
99 &Cookie{Name: "special-3", Value: "a "},
100 `special-3="a "`,
101 },
102 {
103 &Cookie{Name: "special-4", Value: " "},
104 `special-4=" "`,
105 },
106 {
107 &Cookie{Name: "special-5", Value: "a,z"},
108 `special-5="a,z"`,
109 },
110 {
111 &Cookie{Name: "special-6", Value: ",z"},
112 `special-6=",z"`,
113 },
114 {
115 &Cookie{Name: "special-7", Value: "a,"},
116 `special-7="a,"`,
117 },
118 {
119 &Cookie{Name: "special-8", Value: ","},
120 `special-8=","`,
121 },
122 {
123 &Cookie{Name: "empty-value", Value: ""},
124 `empty-value=`,
125 },
126 {
127 nil,
128 ``,
129 },
130 {
131 &Cookie{Name: ""},
132 ``,
133 },
134 {
135 &Cookie{Name: "\t"},
136 ``,
137 },
138 {
139 &Cookie{Name: "\r"},
140 ``,
141 },
142 {
143 &Cookie{Name: "a\nb", Value: "v"},
144 ``,
145 },
146 {
147 &Cookie{Name: "a\nb", Value: "v"},
148 ``,
149 },
150 {
151 &Cookie{Name: "a\rb", Value: "v"},
152 ``,
153 },
154
155 {
156 &Cookie{Name: "cookie", Value: "quoted", Quoted: true},
157 `cookie="quoted"`,
158 },
159 {
160 &Cookie{Name: "cookie", Value: "quoted with spaces", Quoted: true},
161 `cookie="quoted with spaces"`,
162 },
163 {
164 &Cookie{Name: "cookie", Value: "quoted,with,commas", Quoted: true},
165 `cookie="quoted,with,commas"`,
166 },
167 }
168
169 func TestWriteSetCookies(t *testing.T) {
170 defer log.SetOutput(os.Stderr)
171 var logbuf strings.Builder
172 log.SetOutput(&logbuf)
173
174 for i, tt := range writeSetCookiesTests {
175 if g, e := tt.Cookie.String(), tt.Raw; g != e {
176 t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g)
177 }
178 }
179
180 if got, sub := logbuf.String(), "dropping domain attribute"; !strings.Contains(got, sub) {
181 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
182 }
183 }
184
185 type headerOnlyResponseWriter Header
186
187 func (ho headerOnlyResponseWriter) Header() Header {
188 return Header(ho)
189 }
190
191 func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
192 panic("NOIMPL")
193 }
194
195 func (ho headerOnlyResponseWriter) WriteHeader(int) {
196 panic("NOIMPL")
197 }
198
199 func TestSetCookie(t *testing.T) {
200 m := make(Header)
201 SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
202 SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
203 if l := len(m["Set-Cookie"]); l != 2 {
204 t.Fatalf("expected %d cookies, got %d", 2, l)
205 }
206 if g, e := m["Set-Cookie"][0], "cookie-1=one; Path=/restricted/"; g != e {
207 t.Errorf("cookie #1: want %q, got %q", e, g)
208 }
209 if g, e := m["Set-Cookie"][1], "cookie-2=two; Max-Age=3600"; g != e {
210 t.Errorf("cookie #2: want %q, got %q", e, g)
211 }
212 }
213
214 var addCookieTests = []struct {
215 Cookies []*Cookie
216 Raw string
217 }{
218 {
219 []*Cookie{},
220 "",
221 },
222 {
223 []*Cookie{{Name: "cookie-1", Value: "v$1"}},
224 "cookie-1=v$1",
225 },
226 {
227 []*Cookie{
228 {Name: "cookie-1", Value: "v$1"},
229 {Name: "cookie-2", Value: "v$2"},
230 {Name: "cookie-3", Value: "v$3"},
231 },
232 "cookie-1=v$1; cookie-2=v$2; cookie-3=v$3",
233 },
234
235 {
236 []*Cookie{
237 {Name: "cookie-1", Value: "quoted", Quoted: true},
238 {Name: "cookie-2", Value: "quoted with spaces", Quoted: true},
239 {Name: "cookie-3", Value: "quoted,with,commas", Quoted: true},
240 },
241 `cookie-1="quoted"; cookie-2="quoted with spaces"; cookie-3="quoted,with,commas"`,
242 },
243 }
244
245 func TestAddCookie(t *testing.T) {
246 for i, tt := range addCookieTests {
247 req, _ := NewRequest("GET", "http://example.com/", nil)
248 for _, c := range tt.Cookies {
249 req.AddCookie(c)
250 }
251 if g := req.Header.Get("Cookie"); g != tt.Raw {
252 t.Errorf("Test %d:\nwant: %s\n got: %s\n", i, tt.Raw, g)
253 }
254 }
255 }
256
257 var readSetCookiesTests = []struct {
258 Header Header
259 Cookies []*Cookie
260 }{
261 {
262 Header{"Set-Cookie": {"Cookie-1=v$1"}},
263 []*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
264 },
265 {
266 Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
267 []*Cookie{{
268 Name: "NID",
269 Value: "99=YsDT5i3E-CXax-",
270 Path: "/",
271 Domain: ".google.ch",
272 HttpOnly: true,
273 Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
274 RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
275 Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
276 }},
277 },
278 {
279 Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
280 []*Cookie{{
281 Name: ".ASPXAUTH",
282 Value: "7E3AA",
283 Path: "/",
284 Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
285 RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
286 HttpOnly: true,
287 Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
288 }},
289 },
290 {
291 Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
292 []*Cookie{{
293 Name: "ASP.NET_SessionId",
294 Value: "foo",
295 Path: "/",
296 HttpOnly: true,
297 Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly",
298 }},
299 },
300 {
301 Header{"Set-Cookie": {"samesitedefault=foo; SameSite"}},
302 []*Cookie{{
303 Name: "samesitedefault",
304 Value: "foo",
305 SameSite: SameSiteDefaultMode,
306 Raw: "samesitedefault=foo; SameSite",
307 }},
308 },
309 {
310 Header{"Set-Cookie": {"samesiteinvalidisdefault=foo; SameSite=invalid"}},
311 []*Cookie{{
312 Name: "samesiteinvalidisdefault",
313 Value: "foo",
314 SameSite: SameSiteDefaultMode,
315 Raw: "samesiteinvalidisdefault=foo; SameSite=invalid",
316 }},
317 },
318 {
319 Header{"Set-Cookie": {"samesitelax=foo; SameSite=Lax"}},
320 []*Cookie{{
321 Name: "samesitelax",
322 Value: "foo",
323 SameSite: SameSiteLaxMode,
324 Raw: "samesitelax=foo; SameSite=Lax",
325 }},
326 },
327 {
328 Header{"Set-Cookie": {"samesitestrict=foo; SameSite=Strict"}},
329 []*Cookie{{
330 Name: "samesitestrict",
331 Value: "foo",
332 SameSite: SameSiteStrictMode,
333 Raw: "samesitestrict=foo; SameSite=Strict",
334 }},
335 },
336 {
337 Header{"Set-Cookie": {"samesitenone=foo; SameSite=None"}},
338 []*Cookie{{
339 Name: "samesitenone",
340 Value: "foo",
341 SameSite: SameSiteNoneMode,
342 Raw: "samesitenone=foo; SameSite=None",
343 }},
344 },
345
346
347 {
348 Header{"Set-Cookie": {`special-1=a z`}},
349 []*Cookie{{Name: "special-1", Value: "a z", Raw: `special-1=a z`}},
350 },
351 {
352 Header{"Set-Cookie": {`special-2=" z"`}},
353 []*Cookie{{Name: "special-2", Value: " z", Quoted: true, Raw: `special-2=" z"`}},
354 },
355 {
356 Header{"Set-Cookie": {`special-3="a "`}},
357 []*Cookie{{Name: "special-3", Value: "a ", Quoted: true, Raw: `special-3="a "`}},
358 },
359 {
360 Header{"Set-Cookie": {`special-4=" "`}},
361 []*Cookie{{Name: "special-4", Value: " ", Quoted: true, Raw: `special-4=" "`}},
362 },
363 {
364 Header{"Set-Cookie": {`special-5=a,z`}},
365 []*Cookie{{Name: "special-5", Value: "a,z", Raw: `special-5=a,z`}},
366 },
367 {
368 Header{"Set-Cookie": {`special-6=",z"`}},
369 []*Cookie{{Name: "special-6", Value: ",z", Quoted: true, Raw: `special-6=",z"`}},
370 },
371 {
372 Header{"Set-Cookie": {`special-7=a,`}},
373 []*Cookie{{Name: "special-7", Value: "a,", Raw: `special-7=a,`}},
374 },
375 {
376 Header{"Set-Cookie": {`special-8=","`}},
377 []*Cookie{{Name: "special-8", Value: ",", Quoted: true, Raw: `special-8=","`}},
378 },
379
380
381 {
382 Header{"Set-Cookie": {`special-9 =","`}},
383 []*Cookie{{Name: "special-9", Value: ",", Quoted: true, Raw: `special-9 =","`}},
384 },
385
386 {
387 Header{"Set-Cookie": {`cookie="quoted"`}},
388 []*Cookie{{Name: "cookie", Value: "quoted", Quoted: true, Raw: `cookie="quoted"`}},
389 },
390
391
392
393
394
395
396 }
397
398 func toJSON(v any) string {
399 b, err := json.Marshal(v)
400 if err != nil {
401 return fmt.Sprintf("%#v", v)
402 }
403 return string(b)
404 }
405
406 func TestReadSetCookies(t *testing.T) {
407 for i, tt := range readSetCookiesTests {
408 for n := 0; n < 2; n++ {
409 c := readSetCookies(tt.Header)
410 if !reflect.DeepEqual(c, tt.Cookies) {
411 t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
412 }
413 }
414 }
415 }
416
417 var readCookiesTests = []struct {
418 Header Header
419 Filter string
420 Cookies []*Cookie
421 }{
422 {
423 Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
424 "",
425 []*Cookie{
426 {Name: "Cookie-1", Value: "v$1"},
427 {Name: "c2", Value: "v2"},
428 },
429 },
430 {
431 Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
432 "c2",
433 []*Cookie{
434 {Name: "c2", Value: "v2"},
435 },
436 },
437 {
438 Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
439 "",
440 []*Cookie{
441 {Name: "Cookie-1", Value: "v$1"},
442 {Name: "c2", Value: "v2"},
443 },
444 },
445 {
446 Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
447 "c2",
448 []*Cookie{
449 {Name: "c2", Value: "v2"},
450 },
451 },
452 {
453 Header{"Cookie": {`Cookie-1="v$1"; c2="v2"`}},
454 "",
455 []*Cookie{
456 {Name: "Cookie-1", Value: "v$1", Quoted: true},
457 {Name: "c2", Value: "v2", Quoted: true},
458 },
459 },
460 {
461 Header{"Cookie": {`Cookie-1="v$1"; c2=v2;`}},
462 "",
463 []*Cookie{
464 {Name: "Cookie-1", Value: "v$1", Quoted: true},
465 {Name: "c2", Value: "v2"},
466 },
467 },
468 {
469 Header{"Cookie": {``}},
470 "",
471 []*Cookie{},
472 },
473 }
474
475 func TestReadCookies(t *testing.T) {
476 for i, tt := range readCookiesTests {
477 for n := 0; n < 2; n++ {
478 c := readCookies(tt.Header, tt.Filter)
479 if !reflect.DeepEqual(c, tt.Cookies) {
480 t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
481 }
482 }
483 }
484 }
485
486 func TestSetCookieDoubleQuotes(t *testing.T) {
487 res := &Response{Header: Header{}}
488 res.Header.Add("Set-Cookie", `quoted0=none; max-age=30`)
489 res.Header.Add("Set-Cookie", `quoted1="cookieValue"; max-age=31`)
490 res.Header.Add("Set-Cookie", `quoted2=cookieAV; max-age="32"`)
491 res.Header.Add("Set-Cookie", `quoted3="both"; max-age="33"`)
492 got := res.Cookies()
493 want := []*Cookie{
494 {Name: "quoted0", Value: "none", MaxAge: 30},
495 {Name: "quoted1", Value: "cookieValue", MaxAge: 31},
496 {Name: "quoted2", Value: "cookieAV"},
497 {Name: "quoted3", Value: "both"},
498 }
499 if len(got) != len(want) {
500 t.Fatalf("got %d cookies, want %d", len(got), len(want))
501 }
502 for i, w := range want {
503 g := got[i]
504 if g.Name != w.Name || g.Value != w.Value || g.MaxAge != w.MaxAge {
505 t.Errorf("cookie #%d:\ngot %v\nwant %v", i, g, w)
506 }
507 }
508 }
509
510 func TestCookieSanitizeValue(t *testing.T) {
511 defer log.SetOutput(os.Stderr)
512 var logbuf strings.Builder
513 log.SetOutput(&logbuf)
514
515 tests := []struct {
516 in string
517 quoted bool
518 want string
519 }{
520 {"foo", false, "foo"},
521 {"foo;bar", false, "foobar"},
522 {"foo\\bar", false, "foobar"},
523 {"foo\"bar", false, "foobar"},
524 {"\x00\x7e\x7f\x80", false, "\x7e"},
525 {`withquotes`, true, `"withquotes"`},
526 {`"withquotes"`, true, `"withquotes"`},
527 {"a z", false, `"a z"`},
528 {" z", false, `" z"`},
529 {"a ", false, `"a "`},
530 {"a,z", false, `"a,z"`},
531 {",z", false, `",z"`},
532 {"a,", false, `"a,"`},
533 {"", true, `""`},
534 }
535 for _, tt := range tests {
536 if got := sanitizeCookieValue(tt.in, tt.quoted); got != tt.want {
537 t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want)
538 }
539 }
540
541 if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
542 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
543 }
544 }
545
546 func TestCookieSanitizePath(t *testing.T) {
547 defer log.SetOutput(os.Stderr)
548 var logbuf strings.Builder
549 log.SetOutput(&logbuf)
550
551 tests := []struct {
552 in, want string
553 }{
554 {"/path", "/path"},
555 {"/path with space/", "/path with space/"},
556 {"/just;no;semicolon\x00orstuff/", "/justnosemicolonorstuff/"},
557 }
558 for _, tt := range tests {
559 if got := sanitizeCookiePath(tt.in); got != tt.want {
560 t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want)
561 }
562 }
563
564 if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
565 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
566 }
567 }
568
569 func TestCookieValid(t *testing.T) {
570 tests := []struct {
571 cookie *Cookie
572 valid bool
573 }{
574 {nil, false},
575 {&Cookie{Name: ""}, false},
576 {&Cookie{Name: "invalid-value", Value: "foo\"bar"}, false},
577 {&Cookie{Name: "invalid-path", Path: "/foo;bar/"}, false},
578 {&Cookie{Name: "invalid-secure-for-partitioned", Value: "foo", Path: "/", Secure: false, Partitioned: true}, false},
579 {&Cookie{Name: "invalid-domain", Domain: "example.com:80"}, false},
580 {&Cookie{Name: "invalid-expiry", Value: "", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)}, false},
581 {&Cookie{Name: "valid-empty"}, true},
582 {&Cookie{Name: "valid-expires", Value: "foo", Path: "/bar", Domain: "example.com", Expires: time.Unix(0, 0)}, true},
583 {&Cookie{Name: "valid-max-age", Value: "foo", Path: "/bar", Domain: "example.com", MaxAge: 60}, true},
584 {&Cookie{Name: "valid-all-fields", Value: "foo", Path: "/bar", Domain: "example.com", Expires: time.Unix(0, 0), MaxAge: 0}, true},
585 {&Cookie{Name: "valid-partitioned", Value: "foo", Path: "/", Secure: true, Partitioned: true}, true},
586 }
587
588 for _, tt := range tests {
589 err := tt.cookie.Valid()
590 if err != nil && tt.valid {
591 t.Errorf("%#v.Valid() returned error %v; want nil", tt.cookie, err)
592 }
593 if err == nil && !tt.valid {
594 t.Errorf("%#v.Valid() returned nil; want error", tt.cookie)
595 }
596 }
597 }
598
599 func BenchmarkCookieString(b *testing.B) {
600 const wantCookieString = `cookie-9=i3e01nf61b6t23bvfmplnanol3; Path=/restricted/; Domain=example.com; Expires=Tue, 10 Nov 2009 23:00:00 GMT; Max-Age=3600`
601 c := &Cookie{
602 Name: "cookie-9",
603 Value: "i3e01nf61b6t23bvfmplnanol3",
604 Expires: time.Unix(1257894000, 0),
605 Path: "/restricted/",
606 Domain: ".example.com",
607 MaxAge: 3600,
608 }
609 var benchmarkCookieString string
610 b.ReportAllocs()
611 b.ResetTimer()
612 for i := 0; i < b.N; i++ {
613 benchmarkCookieString = c.String()
614 }
615 if have, want := benchmarkCookieString, wantCookieString; have != want {
616 b.Fatalf("Have: %v Want: %v", have, want)
617 }
618 }
619
620 func BenchmarkReadSetCookies(b *testing.B) {
621 header := Header{
622 "Set-Cookie": {
623 "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
624 ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
625 },
626 }
627 wantCookies := []*Cookie{
628 {
629 Name: "NID",
630 Value: "99=YsDT5i3E-CXax-",
631 Path: "/",
632 Domain: ".google.ch",
633 HttpOnly: true,
634 Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
635 RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
636 Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
637 },
638 {
639 Name: ".ASPXAUTH",
640 Value: "7E3AA",
641 Path: "/",
642 Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
643 RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
644 HttpOnly: true,
645 Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
646 },
647 }
648 var c []*Cookie
649 b.ReportAllocs()
650 b.ResetTimer()
651 for i := 0; i < b.N; i++ {
652 c = readSetCookies(header)
653 }
654 if !reflect.DeepEqual(c, wantCookies) {
655 b.Fatalf("readSetCookies:\nhave: %s\nwant: %s\n", toJSON(c), toJSON(wantCookies))
656 }
657 }
658
659 func BenchmarkReadCookies(b *testing.B) {
660 header := Header{
661 "Cookie": {
662 `de=; client_region=0; rpld1=0:hispeed.ch|20:che|21:zh|22:zurich|23:47.36|24:8.53|; rpld0=1:08|; backplane-channel=newspaper.com:1471; devicetype=0; osfam=0; rplmct=2; s_pers=%20s_vmonthnum%3D1472680800496%2526vn%253D1%7C1472680800496%3B%20s_nr%3D1471686767664-New%7C1474278767664%3B%20s_lv%3D1471686767669%7C1566294767669%3B%20s_lv_s%3DFirst%2520Visit%7C1471688567669%3B%20s_monthinvisit%3Dtrue%7C1471688567677%3B%20gvp_p5%3Dsports%253Ablog%253Aearly-lead%2520-%2520184693%2520-%252020160820%2520-%2520u-s%7C1471688567681%3B%20gvp_p51%3Dwp%2520-%2520sports%7C1471688567684%3B; s_sess=%20s_wp_ep%3Dhomepage%3B%20s._ref%3Dhttps%253A%252F%252Fwww.google.ch%252F%3B%20s_cc%3Dtrue%3B%20s_ppvl%3Dsports%25253Ablog%25253Aearly-lead%252520-%252520184693%252520-%25252020160820%252520-%252520u-lawyer%252C12%252C12%252C502%252C1231%252C502%252C1680%252C1050%252C2%252CP%3B%20s_ppv%3Dsports%25253Ablog%25253Aearly-lead%252520-%252520184693%252520-%25252020160820%252520-%252520u-s-lawyer%252C12%252C12%252C502%252C1231%252C502%252C1680%252C1050%252C2%252CP%3B%20s_dslv%3DFirst%2520Visit%3B%20s_sq%3Dwpninewspapercom%253D%252526pid%25253Dsports%2525253Ablog%2525253Aearly-lead%25252520-%25252520184693%25252520-%2525252020160820%25252520-%25252520u-s%252526pidt%25253D1%252526oid%25253Dhttps%2525253A%2525252F%2525252Fwww.newspaper.com%2525252F%2525253Fnid%2525253Dmenu_nav_homepage%252526ot%25253DA%3B`,
663 },
664 }
665 wantCookies := []*Cookie{
666 {Name: "de", Value: ""},
667 {Name: "client_region", Value: "0"},
668 {Name: "rpld1", Value: "0:hispeed.ch|20:che|21:zh|22:zurich|23:47.36|24:8.53|"},
669 {Name: "rpld0", Value: "1:08|"},
670 {Name: "backplane-channel", Value: "newspaper.com:1471"},
671 {Name: "devicetype", Value: "0"},
672 {Name: "osfam", Value: "0"},
673 {Name: "rplmct", Value: "2"},
674 {Name: "s_pers", Value: "%20s_vmonthnum%3D1472680800496%2526vn%253D1%7C1472680800496%3B%20s_nr%3D1471686767664-New%7C1474278767664%3B%20s_lv%3D1471686767669%7C1566294767669%3B%20s_lv_s%3DFirst%2520Visit%7C1471688567669%3B%20s_monthinvisit%3Dtrue%7C1471688567677%3B%20gvp_p5%3Dsports%253Ablog%253Aearly-lead%2520-%2520184693%2520-%252020160820%2520-%2520u-s%7C1471688567681%3B%20gvp_p51%3Dwp%2520-%2520sports%7C1471688567684%3B"},
675 {Name: "s_sess", Value: "%20s_wp_ep%3Dhomepage%3B%20s._ref%3Dhttps%253A%252F%252Fwww.google.ch%252F%3B%20s_cc%3Dtrue%3B%20s_ppvl%3Dsports%25253Ablog%25253Aearly-lead%252520-%252520184693%252520-%25252020160820%252520-%252520u-lawyer%252C12%252C12%252C502%252C1231%252C502%252C1680%252C1050%252C2%252CP%3B%20s_ppv%3Dsports%25253Ablog%25253Aearly-lead%252520-%252520184693%252520-%25252020160820%252520-%252520u-s-lawyer%252C12%252C12%252C502%252C1231%252C502%252C1680%252C1050%252C2%252CP%3B%20s_dslv%3DFirst%2520Visit%3B%20s_sq%3Dwpninewspapercom%253D%252526pid%25253Dsports%2525253Ablog%2525253Aearly-lead%25252520-%25252520184693%25252520-%2525252020160820%25252520-%25252520u-s%252526pidt%25253D1%252526oid%25253Dhttps%2525253A%2525252F%2525252Fwww.newspaper.com%2525252F%2525253Fnid%2525253Dmenu_nav_homepage%252526ot%25253DA%3B"},
676 }
677 var c []*Cookie
678 b.ReportAllocs()
679 b.ResetTimer()
680 for i := 0; i < b.N; i++ {
681 c = readCookies(header, "")
682 }
683 if !reflect.DeepEqual(c, wantCookies) {
684 b.Fatalf("readCookies:\nhave: %s\nwant: %s\n", toJSON(c), toJSON(wantCookies))
685 }
686 }
687
688 func TestParseCookie(t *testing.T) {
689 tests := []struct {
690 line string
691 cookies []*Cookie
692 err error
693 }{
694 {
695 line: "Cookie-1=v$1",
696 cookies: []*Cookie{{Name: "Cookie-1", Value: "v$1"}},
697 },
698 {
699 line: "Cookie-1=v$1;c2=v2",
700 cookies: []*Cookie{{Name: "Cookie-1", Value: "v$1"}, {Name: "c2", Value: "v2"}},
701 },
702 {
703 line: `Cookie-1="v$1";c2="v2"`,
704 cookies: []*Cookie{{Name: "Cookie-1", Value: "v$1", Quoted: true}, {Name: "c2", Value: "v2", Quoted: true}},
705 },
706 {
707 line: "k1=",
708 cookies: []*Cookie{{Name: "k1", Value: ""}},
709 },
710 {
711 line: "",
712 err: errBlankCookie,
713 },
714 {
715 line: "equal-not-found",
716 err: errEqualNotFoundInCookie,
717 },
718 {
719 line: "=v1",
720 err: errInvalidCookieName,
721 },
722 {
723 line: "k1=\\",
724 err: errInvalidCookieValue,
725 },
726 }
727 for i, tt := range tests {
728 gotCookies, gotErr := ParseCookie(tt.line)
729 if !errors.Is(gotErr, tt.err) {
730 t.Errorf("#%d ParseCookie got error %v, want error %v", i, gotErr, tt.err)
731 }
732 if !reflect.DeepEqual(gotCookies, tt.cookies) {
733 t.Errorf("#%d ParseCookie:\ngot cookies: %s\nwant cookies: %s\n", i, toJSON(gotCookies), toJSON(tt.cookies))
734 }
735 }
736 }
737
738 func TestParseSetCookie(t *testing.T) {
739 tests := []struct {
740 line string
741 cookie *Cookie
742 err error
743 }{
744 {
745 line: "Cookie-1=v$1",
746 cookie: &Cookie{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"},
747 },
748 {
749 line: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
750 cookie: &Cookie{
751 Name: "NID",
752 Value: "99=YsDT5i3E-CXax-",
753 Path: "/",
754 Domain: ".google.ch",
755 HttpOnly: true,
756 Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
757 RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
758 Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
759 },
760 },
761 {
762 line: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
763 cookie: &Cookie{
764 Name: ".ASPXAUTH",
765 Value: "7E3AA",
766 Path: "/",
767 Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
768 RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
769 HttpOnly: true,
770 Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
771 },
772 },
773 {
774 line: "ASP.NET_SessionId=foo; path=/; HttpOnly",
775 cookie: &Cookie{
776 Name: "ASP.NET_SessionId",
777 Value: "foo",
778 Path: "/",
779 HttpOnly: true,
780 Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly",
781 },
782 },
783 {
784 line: "samesitedefault=foo; SameSite",
785 cookie: &Cookie{
786 Name: "samesitedefault",
787 Value: "foo",
788 SameSite: SameSiteDefaultMode,
789 Raw: "samesitedefault=foo; SameSite",
790 },
791 },
792 {
793 line: "samesiteinvalidisdefault=foo; SameSite=invalid",
794 cookie: &Cookie{
795 Name: "samesiteinvalidisdefault",
796 Value: "foo",
797 SameSite: SameSiteDefaultMode,
798 Raw: "samesiteinvalidisdefault=foo; SameSite=invalid",
799 },
800 },
801 {
802 line: "samesitelax=foo; SameSite=Lax",
803 cookie: &Cookie{
804 Name: "samesitelax",
805 Value: "foo",
806 SameSite: SameSiteLaxMode,
807 Raw: "samesitelax=foo; SameSite=Lax",
808 },
809 },
810 {
811 line: "samesitestrict=foo; SameSite=Strict",
812 cookie: &Cookie{
813 Name: "samesitestrict",
814 Value: "foo",
815 SameSite: SameSiteStrictMode,
816 Raw: "samesitestrict=foo; SameSite=Strict",
817 },
818 },
819 {
820 line: "samesitenone=foo; SameSite=None",
821 cookie: &Cookie{
822 Name: "samesitenone",
823 Value: "foo",
824 SameSite: SameSiteNoneMode,
825 Raw: "samesitenone=foo; SameSite=None",
826 },
827 },
828
829
830 {
831 line: `special-1=a z`,
832 cookie: &Cookie{Name: "special-1", Value: "a z", Raw: `special-1=a z`},
833 },
834 {
835 line: `special-2=" z"`,
836 cookie: &Cookie{Name: "special-2", Value: " z", Quoted: true, Raw: `special-2=" z"`},
837 },
838 {
839 line: `special-3="a "`,
840 cookie: &Cookie{Name: "special-3", Value: "a ", Quoted: true, Raw: `special-3="a "`},
841 },
842 {
843 line: `special-4=" "`,
844 cookie: &Cookie{Name: "special-4", Value: " ", Quoted: true, Raw: `special-4=" "`},
845 },
846 {
847 line: `special-5=a,z`,
848 cookie: &Cookie{Name: "special-5", Value: "a,z", Raw: `special-5=a,z`},
849 },
850 {
851 line: `special-6=",z"`,
852 cookie: &Cookie{Name: "special-6", Value: ",z", Quoted: true, Raw: `special-6=",z"`},
853 },
854 {
855 line: `special-7=a,`,
856 cookie: &Cookie{Name: "special-7", Value: "a,", Raw: `special-7=a,`},
857 },
858 {
859 line: `special-8=","`,
860 cookie: &Cookie{Name: "special-8", Value: ",", Quoted: true, Raw: `special-8=","`},
861 },
862
863
864 {
865 line: `special-9 =","`,
866 cookie: &Cookie{Name: "special-9", Value: ",", Quoted: true, Raw: `special-9 =","`},
867 },
868 {
869 line: "",
870 err: errBlankCookie,
871 },
872 {
873 line: "equal-not-found",
874 err: errEqualNotFoundInCookie,
875 },
876 {
877 line: "=v1",
878 err: errInvalidCookieName,
879 },
880 {
881 line: "k1=\\",
882 err: errInvalidCookieValue,
883 },
884 }
885 for i, tt := range tests {
886 gotCookie, gotErr := ParseSetCookie(tt.line)
887 if !errors.Is(gotErr, tt.err) {
888 t.Errorf("#%d ParseSetCookie got error %v, want error %v", i, gotErr, tt.err)
889 continue
890 }
891 if !reflect.DeepEqual(gotCookie, tt.cookie) {
892 t.Errorf("#%d ParseSetCookie:\ngot cookie: %s\nwant cookie: %s\n", i, toJSON(gotCookie), toJSON(tt.cookie))
893 }
894 }
895 }
896
View as plain text