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 }
534 for _, tt := range tests {
535 if got := sanitizeCookieValue(tt.in, tt.quoted); got != tt.want {
536 t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want)
537 }
538 }
539
540 if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
541 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
542 }
543 }
544
545 func TestCookieSanitizePath(t *testing.T) {
546 defer log.SetOutput(os.Stderr)
547 var logbuf strings.Builder
548 log.SetOutput(&logbuf)
549
550 tests := []struct {
551 in, want string
552 }{
553 {"/path", "/path"},
554 {"/path with space/", "/path with space/"},
555 {"/just;no;semicolon\x00orstuff/", "/justnosemicolonorstuff/"},
556 }
557 for _, tt := range tests {
558 if got := sanitizeCookiePath(tt.in); got != tt.want {
559 t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want)
560 }
561 }
562
563 if got, sub := logbuf.String(), "dropping invalid bytes"; !strings.Contains(got, sub) {
564 t.Errorf("Expected substring %q in log output. Got:\n%s", sub, got)
565 }
566 }
567
568 func TestCookieValid(t *testing.T) {
569 tests := []struct {
570 cookie *Cookie
571 valid bool
572 }{
573 {nil, false},
574 {&Cookie{Name: ""}, false},
575 {&Cookie{Name: "invalid-value", Value: "foo\"bar"}, false},
576 {&Cookie{Name: "invalid-path", Path: "/foo;bar/"}, false},
577 {&Cookie{Name: "invalid-secure-for-partitioned", Value: "foo", Path: "/", Secure: false, Partitioned: true}, false},
578 {&Cookie{Name: "invalid-domain", Domain: "example.com:80"}, false},
579 {&Cookie{Name: "invalid-expiry", Value: "", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)}, false},
580 {&Cookie{Name: "valid-empty"}, true},
581 {&Cookie{Name: "valid-expires", Value: "foo", Path: "/bar", Domain: "example.com", Expires: time.Unix(0, 0)}, true},
582 {&Cookie{Name: "valid-max-age", Value: "foo", Path: "/bar", Domain: "example.com", MaxAge: 60}, true},
583 {&Cookie{Name: "valid-all-fields", Value: "foo", Path: "/bar", Domain: "example.com", Expires: time.Unix(0, 0), MaxAge: 0}, true},
584 {&Cookie{Name: "valid-partitioned", Value: "foo", Path: "/", Secure: true, Partitioned: true}, true},
585 }
586
587 for _, tt := range tests {
588 err := tt.cookie.Valid()
589 if err != nil && tt.valid {
590 t.Errorf("%#v.Valid() returned error %v; want nil", tt.cookie, err)
591 }
592 if err == nil && !tt.valid {
593 t.Errorf("%#v.Valid() returned nil; want error", tt.cookie)
594 }
595 }
596 }
597
598 func BenchmarkCookieString(b *testing.B) {
599 const wantCookieString = `cookie-9=i3e01nf61b6t23bvfmplnanol3; Path=/restricted/; Domain=example.com; Expires=Tue, 10 Nov 2009 23:00:00 GMT; Max-Age=3600`
600 c := &Cookie{
601 Name: "cookie-9",
602 Value: "i3e01nf61b6t23bvfmplnanol3",
603 Expires: time.Unix(1257894000, 0),
604 Path: "/restricted/",
605 Domain: ".example.com",
606 MaxAge: 3600,
607 }
608 var benchmarkCookieString string
609 b.ReportAllocs()
610 b.ResetTimer()
611 for i := 0; i < b.N; i++ {
612 benchmarkCookieString = c.String()
613 }
614 if have, want := benchmarkCookieString, wantCookieString; have != want {
615 b.Fatalf("Have: %v Want: %v", have, want)
616 }
617 }
618
619 func BenchmarkReadSetCookies(b *testing.B) {
620 header := Header{
621 "Set-Cookie": {
622 "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
623 ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
624 },
625 }
626 wantCookies := []*Cookie{
627 {
628 Name: "NID",
629 Value: "99=YsDT5i3E-CXax-",
630 Path: "/",
631 Domain: ".google.ch",
632 HttpOnly: true,
633 Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
634 RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
635 Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
636 },
637 {
638 Name: ".ASPXAUTH",
639 Value: "7E3AA",
640 Path: "/",
641 Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
642 RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
643 HttpOnly: true,
644 Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
645 },
646 }
647 var c []*Cookie
648 b.ReportAllocs()
649 b.ResetTimer()
650 for i := 0; i < b.N; i++ {
651 c = readSetCookies(header)
652 }
653 if !reflect.DeepEqual(c, wantCookies) {
654 b.Fatalf("readSetCookies:\nhave: %s\nwant: %s\n", toJSON(c), toJSON(wantCookies))
655 }
656 }
657
658 func BenchmarkReadCookies(b *testing.B) {
659 header := Header{
660 "Cookie": {
661 `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`,
662 },
663 }
664 wantCookies := []*Cookie{
665 {Name: "de", Value: ""},
666 {Name: "client_region", Value: "0"},
667 {Name: "rpld1", Value: "0:hispeed.ch|20:che|21:zh|22:zurich|23:47.36|24:8.53|"},
668 {Name: "rpld0", Value: "1:08|"},
669 {Name: "backplane-channel", Value: "newspaper.com:1471"},
670 {Name: "devicetype", Value: "0"},
671 {Name: "osfam", Value: "0"},
672 {Name: "rplmct", Value: "2"},
673 {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"},
674 {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"},
675 }
676 var c []*Cookie
677 b.ReportAllocs()
678 b.ResetTimer()
679 for i := 0; i < b.N; i++ {
680 c = readCookies(header, "")
681 }
682 if !reflect.DeepEqual(c, wantCookies) {
683 b.Fatalf("readCookies:\nhave: %s\nwant: %s\n", toJSON(c), toJSON(wantCookies))
684 }
685 }
686
687 func TestParseCookie(t *testing.T) {
688 tests := []struct {
689 line string
690 cookies []*Cookie
691 err error
692 }{
693 {
694 line: "Cookie-1=v$1",
695 cookies: []*Cookie{{Name: "Cookie-1", Value: "v$1"}},
696 },
697 {
698 line: "Cookie-1=v$1;c2=v2",
699 cookies: []*Cookie{{Name: "Cookie-1", Value: "v$1"}, {Name: "c2", Value: "v2"}},
700 },
701 {
702 line: `Cookie-1="v$1";c2="v2"`,
703 cookies: []*Cookie{{Name: "Cookie-1", Value: "v$1", Quoted: true}, {Name: "c2", Value: "v2", Quoted: true}},
704 },
705 {
706 line: "k1=",
707 cookies: []*Cookie{{Name: "k1", Value: ""}},
708 },
709 {
710 line: "",
711 err: errBlankCookie,
712 },
713 {
714 line: "equal-not-found",
715 err: errEqualNotFoundInCookie,
716 },
717 {
718 line: "=v1",
719 err: errInvalidCookieName,
720 },
721 {
722 line: "k1=\\",
723 err: errInvalidCookieValue,
724 },
725 }
726 for i, tt := range tests {
727 gotCookies, gotErr := ParseCookie(tt.line)
728 if !errors.Is(gotErr, tt.err) {
729 t.Errorf("#%d ParseCookie got error %v, want error %v", i, gotErr, tt.err)
730 }
731 if !reflect.DeepEqual(gotCookies, tt.cookies) {
732 t.Errorf("#%d ParseCookie:\ngot cookies: %s\nwant cookies: %s\n", i, toJSON(gotCookies), toJSON(tt.cookies))
733 }
734 }
735 }
736
737 func TestParseSetCookie(t *testing.T) {
738 tests := []struct {
739 line string
740 cookie *Cookie
741 err error
742 }{
743 {
744 line: "Cookie-1=v$1",
745 cookie: &Cookie{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"},
746 },
747 {
748 line: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
749 cookie: &Cookie{
750 Name: "NID",
751 Value: "99=YsDT5i3E-CXax-",
752 Path: "/",
753 Domain: ".google.ch",
754 HttpOnly: true,
755 Expires: time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
756 RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
757 Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
758 },
759 },
760 {
761 line: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
762 cookie: &Cookie{
763 Name: ".ASPXAUTH",
764 Value: "7E3AA",
765 Path: "/",
766 Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
767 RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
768 HttpOnly: true,
769 Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
770 },
771 },
772 {
773 line: "ASP.NET_SessionId=foo; path=/; HttpOnly",
774 cookie: &Cookie{
775 Name: "ASP.NET_SessionId",
776 Value: "foo",
777 Path: "/",
778 HttpOnly: true,
779 Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly",
780 },
781 },
782 {
783 line: "samesitedefault=foo; SameSite",
784 cookie: &Cookie{
785 Name: "samesitedefault",
786 Value: "foo",
787 SameSite: SameSiteDefaultMode,
788 Raw: "samesitedefault=foo; SameSite",
789 },
790 },
791 {
792 line: "samesiteinvalidisdefault=foo; SameSite=invalid",
793 cookie: &Cookie{
794 Name: "samesiteinvalidisdefault",
795 Value: "foo",
796 SameSite: SameSiteDefaultMode,
797 Raw: "samesiteinvalidisdefault=foo; SameSite=invalid",
798 },
799 },
800 {
801 line: "samesitelax=foo; SameSite=Lax",
802 cookie: &Cookie{
803 Name: "samesitelax",
804 Value: "foo",
805 SameSite: SameSiteLaxMode,
806 Raw: "samesitelax=foo; SameSite=Lax",
807 },
808 },
809 {
810 line: "samesitestrict=foo; SameSite=Strict",
811 cookie: &Cookie{
812 Name: "samesitestrict",
813 Value: "foo",
814 SameSite: SameSiteStrictMode,
815 Raw: "samesitestrict=foo; SameSite=Strict",
816 },
817 },
818 {
819 line: "samesitenone=foo; SameSite=None",
820 cookie: &Cookie{
821 Name: "samesitenone",
822 Value: "foo",
823 SameSite: SameSiteNoneMode,
824 Raw: "samesitenone=foo; SameSite=None",
825 },
826 },
827
828
829 {
830 line: `special-1=a z`,
831 cookie: &Cookie{Name: "special-1", Value: "a z", Raw: `special-1=a z`},
832 },
833 {
834 line: `special-2=" z"`,
835 cookie: &Cookie{Name: "special-2", Value: " z", Quoted: true, Raw: `special-2=" z"`},
836 },
837 {
838 line: `special-3="a "`,
839 cookie: &Cookie{Name: "special-3", Value: "a ", Quoted: true, Raw: `special-3="a "`},
840 },
841 {
842 line: `special-4=" "`,
843 cookie: &Cookie{Name: "special-4", Value: " ", Quoted: true, Raw: `special-4=" "`},
844 },
845 {
846 line: `special-5=a,z`,
847 cookie: &Cookie{Name: "special-5", Value: "a,z", Raw: `special-5=a,z`},
848 },
849 {
850 line: `special-6=",z"`,
851 cookie: &Cookie{Name: "special-6", Value: ",z", Quoted: true, Raw: `special-6=",z"`},
852 },
853 {
854 line: `special-7=a,`,
855 cookie: &Cookie{Name: "special-7", Value: "a,", Raw: `special-7=a,`},
856 },
857 {
858 line: `special-8=","`,
859 cookie: &Cookie{Name: "special-8", Value: ",", Quoted: true, Raw: `special-8=","`},
860 },
861
862
863 {
864 line: `special-9 =","`,
865 cookie: &Cookie{Name: "special-9", Value: ",", Quoted: true, Raw: `special-9 =","`},
866 },
867 {
868 line: "",
869 err: errBlankCookie,
870 },
871 {
872 line: "equal-not-found",
873 err: errEqualNotFoundInCookie,
874 },
875 {
876 line: "=v1",
877 err: errInvalidCookieName,
878 },
879 {
880 line: "k1=\\",
881 err: errInvalidCookieValue,
882 },
883 }
884 for i, tt := range tests {
885 gotCookie, gotErr := ParseSetCookie(tt.line)
886 if !errors.Is(gotErr, tt.err) {
887 t.Errorf("#%d ParseSetCookie got error %v, want error %v", i, gotErr, tt.err)
888 continue
889 }
890 if !reflect.DeepEqual(gotCookie, tt.cookie) {
891 t.Errorf("#%d ParseSetCookie:\ngot cookie: %s\nwant cookie: %s\n", i, toJSON(gotCookie), toJSON(tt.cookie))
892 }
893 }
894 }
895
View as plain text