github.com/lovishpuri/go-40569/src@v0.0.0-20230519171745-f8623e7c56cf/net/http/cookiejar/jar_test.go (about) 1 // Copyright 2013 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 cookiejar 6 7 import ( 8 "fmt" 9 "net/http" 10 "net/url" 11 "sort" 12 "strings" 13 "testing" 14 "time" 15 ) 16 17 // tNow is the synthetic current time used as now during testing. 18 var tNow = time.Date(2013, 1, 1, 12, 0, 0, 0, time.UTC) 19 20 // testPSL implements PublicSuffixList with just two rules: "co.uk" 21 // and the default rule "*". 22 // The implementation has two intentional bugs: 23 // 24 // PublicSuffix("www.buggy.psl") == "xy" 25 // PublicSuffix("www2.buggy.psl") == "com" 26 type testPSL struct{} 27 28 func (testPSL) String() string { 29 return "testPSL" 30 } 31 func (testPSL) PublicSuffix(d string) string { 32 if d == "co.uk" || strings.HasSuffix(d, ".co.uk") { 33 return "co.uk" 34 } 35 if d == "www.buggy.psl" { 36 return "xy" 37 } 38 if d == "www2.buggy.psl" { 39 return "com" 40 } 41 return d[strings.LastIndex(d, ".")+1:] 42 } 43 44 // newTestJar creates an empty Jar with testPSL as the public suffix list. 45 func newTestJar() *Jar { 46 jar, err := New(&Options{PublicSuffixList: testPSL{}}) 47 if err != nil { 48 panic(err) 49 } 50 return jar 51 } 52 53 var hasDotSuffixTests = [...]struct { 54 s, suffix string 55 }{ 56 {"", ""}, 57 {"", "."}, 58 {"", "x"}, 59 {".", ""}, 60 {".", "."}, 61 {".", ".."}, 62 {".", "x"}, 63 {".", "x."}, 64 {".", ".x"}, 65 {".", ".x."}, 66 {"x", ""}, 67 {"x", "."}, 68 {"x", ".."}, 69 {"x", "x"}, 70 {"x", "x."}, 71 {"x", ".x"}, 72 {"x", ".x."}, 73 {".x", ""}, 74 {".x", "."}, 75 {".x", ".."}, 76 {".x", "x"}, 77 {".x", "x."}, 78 {".x", ".x"}, 79 {".x", ".x."}, 80 {"x.", ""}, 81 {"x.", "."}, 82 {"x.", ".."}, 83 {"x.", "x"}, 84 {"x.", "x."}, 85 {"x.", ".x"}, 86 {"x.", ".x."}, 87 {"com", ""}, 88 {"com", "m"}, 89 {"com", "om"}, 90 {"com", "com"}, 91 {"com", ".com"}, 92 {"com", "x.com"}, 93 {"com", "xcom"}, 94 {"com", "xorg"}, 95 {"com", "org"}, 96 {"com", "rg"}, 97 {"foo.com", ""}, 98 {"foo.com", "m"}, 99 {"foo.com", "om"}, 100 {"foo.com", "com"}, 101 {"foo.com", ".com"}, 102 {"foo.com", "o.com"}, 103 {"foo.com", "oo.com"}, 104 {"foo.com", "foo.com"}, 105 {"foo.com", ".foo.com"}, 106 {"foo.com", "x.foo.com"}, 107 {"foo.com", "xfoo.com"}, 108 {"foo.com", "xfoo.org"}, 109 {"foo.com", "foo.org"}, 110 {"foo.com", "oo.org"}, 111 {"foo.com", "o.org"}, 112 {"foo.com", ".org"}, 113 {"foo.com", "org"}, 114 {"foo.com", "rg"}, 115 } 116 117 func TestHasDotSuffix(t *testing.T) { 118 for _, tc := range hasDotSuffixTests { 119 got := hasDotSuffix(tc.s, tc.suffix) 120 want := strings.HasSuffix(tc.s, "."+tc.suffix) 121 if got != want { 122 t.Errorf("s=%q, suffix=%q: got %v, want %v", tc.s, tc.suffix, got, want) 123 } 124 } 125 } 126 127 var canonicalHostTests = map[string]string{ 128 "www.example.com": "www.example.com", 129 "WWW.EXAMPLE.COM": "www.example.com", 130 "wWw.eXAmple.CoM": "www.example.com", 131 "www.example.com:80": "www.example.com", 132 "192.168.0.10": "192.168.0.10", 133 "192.168.0.5:8080": "192.168.0.5", 134 "2001:4860:0:2001::68": "2001:4860:0:2001::68", 135 "[2001:4860:0:::68]:8080": "2001:4860:0:::68", 136 "www.bücher.de": "www.xn--bcher-kva.de", 137 "www.example.com.": "www.example.com", 138 // TODO: Fix canonicalHost so that all of the following malformed 139 // domain names trigger an error. (This list is not exhaustive, e.g. 140 // malformed internationalized domain names are missing.) 141 ".": "", 142 "..": ".", 143 "...": "..", 144 ".net": ".net", 145 ".net.": ".net", 146 "a..": "a.", 147 "b.a..": "b.a.", 148 "weird.stuff...": "weird.stuff..", 149 "[bad.unmatched.bracket:": "error", 150 } 151 152 func TestCanonicalHost(t *testing.T) { 153 for h, want := range canonicalHostTests { 154 got, err := canonicalHost(h) 155 if want == "error" { 156 if err == nil { 157 t.Errorf("%q: got %q and nil error, want non-nil", h, got) 158 } 159 continue 160 } 161 if err != nil { 162 t.Errorf("%q: %v", h, err) 163 continue 164 } 165 if got != want { 166 t.Errorf("%q: got %q, want %q", h, got, want) 167 continue 168 } 169 } 170 } 171 172 var hasPortTests = map[string]bool{ 173 "www.example.com": false, 174 "www.example.com:80": true, 175 "127.0.0.1": false, 176 "127.0.0.1:8080": true, 177 "2001:4860:0:2001::68": false, 178 "[2001::0:::68]:80": true, 179 } 180 181 func TestHasPort(t *testing.T) { 182 for host, want := range hasPortTests { 183 if got := hasPort(host); got != want { 184 t.Errorf("%q: got %t, want %t", host, got, want) 185 } 186 } 187 } 188 189 var jarKeyTests = map[string]string{ 190 "foo.www.example.com": "example.com", 191 "www.example.com": "example.com", 192 "example.com": "example.com", 193 "com": "com", 194 "foo.www.bbc.co.uk": "bbc.co.uk", 195 "www.bbc.co.uk": "bbc.co.uk", 196 "bbc.co.uk": "bbc.co.uk", 197 "co.uk": "co.uk", 198 "uk": "uk", 199 "192.168.0.5": "192.168.0.5", 200 "www.buggy.psl": "www.buggy.psl", 201 "www2.buggy.psl": "buggy.psl", 202 // The following are actual outputs of canonicalHost for 203 // malformed inputs to canonicalHost (see above). 204 "": "", 205 ".": ".", 206 "..": ".", 207 ".net": ".net", 208 "a.": "a.", 209 "b.a.": "a.", 210 "weird.stuff..": ".", 211 } 212 213 func TestJarKey(t *testing.T) { 214 for host, want := range jarKeyTests { 215 if got := jarKey(host, testPSL{}); got != want { 216 t.Errorf("%q: got %q, want %q", host, got, want) 217 } 218 } 219 } 220 221 var jarKeyNilPSLTests = map[string]string{ 222 "foo.www.example.com": "example.com", 223 "www.example.com": "example.com", 224 "example.com": "example.com", 225 "com": "com", 226 "foo.www.bbc.co.uk": "co.uk", 227 "www.bbc.co.uk": "co.uk", 228 "bbc.co.uk": "co.uk", 229 "co.uk": "co.uk", 230 "uk": "uk", 231 "192.168.0.5": "192.168.0.5", 232 // The following are actual outputs of canonicalHost for 233 // malformed inputs to canonicalHost. 234 "": "", 235 ".": ".", 236 "..": "..", 237 ".net": ".net", 238 "a.": "a.", 239 "b.a.": "a.", 240 "weird.stuff..": "stuff..", 241 } 242 243 func TestJarKeyNilPSL(t *testing.T) { 244 for host, want := range jarKeyNilPSLTests { 245 if got := jarKey(host, nil); got != want { 246 t.Errorf("%q: got %q, want %q", host, got, want) 247 } 248 } 249 } 250 251 var isIPTests = map[string]bool{ 252 "127.0.0.1": true, 253 "1.2.3.4": true, 254 "2001:4860:0:2001::68": true, 255 "example.com": false, 256 "1.1.1.300": false, 257 "www.foo.bar.net": false, 258 "123.foo.bar.net": false, 259 } 260 261 func TestIsIP(t *testing.T) { 262 for host, want := range isIPTests { 263 if got := isIP(host); got != want { 264 t.Errorf("%q: got %t, want %t", host, got, want) 265 } 266 } 267 } 268 269 var defaultPathTests = map[string]string{ 270 "/": "/", 271 "/abc": "/", 272 "/abc/": "/abc", 273 "/abc/xyz": "/abc", 274 "/abc/xyz/": "/abc/xyz", 275 "/a/b/c.html": "/a/b", 276 "": "/", 277 "strange": "/", 278 "//": "/", 279 "/a//b": "/a/", 280 "/a/./b": "/a/.", 281 "/a/../b": "/a/..", 282 } 283 284 func TestDefaultPath(t *testing.T) { 285 for path, want := range defaultPathTests { 286 if got := defaultPath(path); got != want { 287 t.Errorf("%q: got %q, want %q", path, got, want) 288 } 289 } 290 } 291 292 var domainAndTypeTests = [...]struct { 293 host string // host Set-Cookie header was received from 294 domain string // domain attribute in Set-Cookie header 295 wantDomain string // expected domain of cookie 296 wantHostOnly bool // expected host-cookie flag 297 wantErr error // expected error 298 }{ 299 {"www.example.com", "", "www.example.com", true, nil}, 300 {"127.0.0.1", "", "127.0.0.1", true, nil}, 301 {"2001:4860:0:2001::68", "", "2001:4860:0:2001::68", true, nil}, 302 {"www.example.com", "example.com", "example.com", false, nil}, 303 {"www.example.com", ".example.com", "example.com", false, nil}, 304 {"www.example.com", "www.example.com", "www.example.com", false, nil}, 305 {"www.example.com", ".www.example.com", "www.example.com", false, nil}, 306 {"foo.sso.example.com", "sso.example.com", "sso.example.com", false, nil}, 307 {"bar.co.uk", "bar.co.uk", "bar.co.uk", false, nil}, 308 {"foo.bar.co.uk", ".bar.co.uk", "bar.co.uk", false, nil}, 309 {"127.0.0.1", "127.0.0.1", "127.0.0.1", true, nil}, 310 {"2001:4860:0:2001::68", "2001:4860:0:2001::68", "2001:4860:0:2001::68", true, nil}, 311 {"www.example.com", ".", "", false, errMalformedDomain}, 312 {"www.example.com", "..", "", false, errMalformedDomain}, 313 {"www.example.com", "other.com", "", false, errIllegalDomain}, 314 {"www.example.com", "com", "", false, errIllegalDomain}, 315 {"www.example.com", ".com", "", false, errIllegalDomain}, 316 {"foo.bar.co.uk", ".co.uk", "", false, errIllegalDomain}, 317 {"127.www.0.0.1", "127.0.0.1", "", false, errIllegalDomain}, 318 {"com", "", "com", true, nil}, 319 {"com", "com", "com", true, nil}, 320 {"com", ".com", "com", true, nil}, 321 {"co.uk", "", "co.uk", true, nil}, 322 {"co.uk", "co.uk", "co.uk", true, nil}, 323 {"co.uk", ".co.uk", "co.uk", true, nil}, 324 } 325 326 func TestDomainAndType(t *testing.T) { 327 jar := newTestJar() 328 for _, tc := range domainAndTypeTests { 329 domain, hostOnly, err := jar.domainAndType(tc.host, tc.domain) 330 if err != tc.wantErr { 331 t.Errorf("%q/%q: got %q error, want %v", 332 tc.host, tc.domain, err, tc.wantErr) 333 continue 334 } 335 if err != nil { 336 continue 337 } 338 if domain != tc.wantDomain || hostOnly != tc.wantHostOnly { 339 t.Errorf("%q/%q: got %q/%t want %q/%t", 340 tc.host, tc.domain, domain, hostOnly, 341 tc.wantDomain, tc.wantHostOnly) 342 } 343 } 344 } 345 346 // expiresIn creates an expires attribute delta seconds from tNow. 347 func expiresIn(delta int) string { 348 t := tNow.Add(time.Duration(delta) * time.Second) 349 return "expires=" + t.Format(time.RFC1123) 350 } 351 352 // mustParseURL parses s to a URL and panics on error. 353 func mustParseURL(s string) *url.URL { 354 u, err := url.Parse(s) 355 if err != nil || u.Scheme == "" || u.Host == "" { 356 panic(fmt.Sprintf("Unable to parse URL %s.", s)) 357 } 358 return u 359 } 360 361 // jarTest encapsulates the following actions on a jar: 362 // 1. Perform SetCookies with fromURL and the cookies from setCookies. 363 // (Done at time tNow + 0 ms.) 364 // 2. Check that the entries in the jar matches content. 365 // (Done at time tNow + 1001 ms.) 366 // 3. For each query in tests: Check that Cookies with toURL yields the 367 // cookies in want. 368 // (Query n done at tNow + (n+2)*1001 ms.) 369 type jarTest struct { 370 description string // The description of what this test is supposed to test 371 fromURL string // The full URL of the request from which Set-Cookie headers where received 372 setCookies []string // All the cookies received from fromURL 373 content string // The whole (non-expired) content of the jar 374 queries []query // Queries to test the Jar.Cookies method 375 } 376 377 // query contains one test of the cookies returned from Jar.Cookies. 378 type query struct { 379 toURL string // the URL in the Cookies call 380 want string // the expected list of cookies (order matters) 381 } 382 383 // run runs the jarTest. 384 func (test jarTest) run(t *testing.T, jar *Jar) { 385 now := tNow 386 387 // Populate jar with cookies. 388 setCookies := make([]*http.Cookie, len(test.setCookies)) 389 for i, cs := range test.setCookies { 390 cookies := (&http.Response{Header: http.Header{"Set-Cookie": {cs}}}).Cookies() 391 if len(cookies) != 1 { 392 panic(fmt.Sprintf("Wrong cookie line %q: %#v", cs, cookies)) 393 } 394 setCookies[i] = cookies[0] 395 } 396 jar.setCookies(mustParseURL(test.fromURL), setCookies, now) 397 now = now.Add(1001 * time.Millisecond) 398 399 // Serialize non-expired entries in the form "name1=val1 name2=val2". 400 var cs []string 401 for _, submap := range jar.entries { 402 for _, cookie := range submap { 403 if !cookie.Expires.After(now) { 404 continue 405 } 406 cs = append(cs, cookie.Name+"="+cookie.Value) 407 } 408 } 409 sort.Strings(cs) 410 got := strings.Join(cs, " ") 411 412 // Make sure jar content matches our expectations. 413 if got != test.content { 414 t.Errorf("Test %q Content\ngot %q\nwant %q", 415 test.description, got, test.content) 416 } 417 418 // Test different calls to Cookies. 419 for i, query := range test.queries { 420 now = now.Add(1001 * time.Millisecond) 421 var s []string 422 for _, c := range jar.cookies(mustParseURL(query.toURL), now) { 423 s = append(s, c.Name+"="+c.Value) 424 } 425 if got := strings.Join(s, " "); got != query.want { 426 t.Errorf("Test %q #%d\ngot %q\nwant %q", test.description, i, got, query.want) 427 } 428 } 429 } 430 431 // basicsTests contains fundamental tests. Each jarTest has to be performed on 432 // a fresh, empty Jar. 433 var basicsTests = [...]jarTest{ 434 { 435 "Retrieval of a plain host cookie.", 436 "http://www.host.test/", 437 []string{"A=a"}, 438 "A=a", 439 []query{ 440 {"http://www.host.test", "A=a"}, 441 {"http://www.host.test/", "A=a"}, 442 {"http://www.host.test/some/path", "A=a"}, 443 {"https://www.host.test", "A=a"}, 444 {"https://www.host.test/", "A=a"}, 445 {"https://www.host.test/some/path", "A=a"}, 446 {"ftp://www.host.test", ""}, 447 {"ftp://www.host.test/", ""}, 448 {"ftp://www.host.test/some/path", ""}, 449 {"http://www.other.org", ""}, 450 {"http://sibling.host.test", ""}, 451 {"http://deep.www.host.test", ""}, 452 }, 453 }, 454 { 455 "Secure cookies are not returned to http.", 456 "http://www.host.test/", 457 []string{"A=a; secure"}, 458 "A=a", 459 []query{ 460 {"http://www.host.test", ""}, 461 {"http://www.host.test/", ""}, 462 {"http://www.host.test/some/path", ""}, 463 {"https://www.host.test", "A=a"}, 464 {"https://www.host.test/", "A=a"}, 465 {"https://www.host.test/some/path", "A=a"}, 466 }, 467 }, 468 { 469 "Explicit path.", 470 "http://www.host.test/", 471 []string{"A=a; path=/some/path"}, 472 "A=a", 473 []query{ 474 {"http://www.host.test", ""}, 475 {"http://www.host.test/", ""}, 476 {"http://www.host.test/some", ""}, 477 {"http://www.host.test/some/", ""}, 478 {"http://www.host.test/some/path", "A=a"}, 479 {"http://www.host.test/some/paths", ""}, 480 {"http://www.host.test/some/path/foo", "A=a"}, 481 {"http://www.host.test/some/path/foo/", "A=a"}, 482 }, 483 }, 484 { 485 "Implicit path #1: path is a directory.", 486 "http://www.host.test/some/path/", 487 []string{"A=a"}, 488 "A=a", 489 []query{ 490 {"http://www.host.test", ""}, 491 {"http://www.host.test/", ""}, 492 {"http://www.host.test/some", ""}, 493 {"http://www.host.test/some/", ""}, 494 {"http://www.host.test/some/path", "A=a"}, 495 {"http://www.host.test/some/paths", ""}, 496 {"http://www.host.test/some/path/foo", "A=a"}, 497 {"http://www.host.test/some/path/foo/", "A=a"}, 498 }, 499 }, 500 { 501 "Implicit path #2: path is not a directory.", 502 "http://www.host.test/some/path/index.html", 503 []string{"A=a"}, 504 "A=a", 505 []query{ 506 {"http://www.host.test", ""}, 507 {"http://www.host.test/", ""}, 508 {"http://www.host.test/some", ""}, 509 {"http://www.host.test/some/", ""}, 510 {"http://www.host.test/some/path", "A=a"}, 511 {"http://www.host.test/some/paths", ""}, 512 {"http://www.host.test/some/path/foo", "A=a"}, 513 {"http://www.host.test/some/path/foo/", "A=a"}, 514 }, 515 }, 516 { 517 "Implicit path #3: no path in URL at all.", 518 "http://www.host.test", 519 []string{"A=a"}, 520 "A=a", 521 []query{ 522 {"http://www.host.test", "A=a"}, 523 {"http://www.host.test/", "A=a"}, 524 {"http://www.host.test/some/path", "A=a"}, 525 }, 526 }, 527 { 528 "Cookies are sorted by path length.", 529 "http://www.host.test/", 530 []string{ 531 "A=a; path=/foo/bar", 532 "B=b; path=/foo/bar/baz/qux", 533 "C=c; path=/foo/bar/baz", 534 "D=d; path=/foo"}, 535 "A=a B=b C=c D=d", 536 []query{ 537 {"http://www.host.test/foo/bar/baz/qux", "B=b C=c A=a D=d"}, 538 {"http://www.host.test/foo/bar/baz/", "C=c A=a D=d"}, 539 {"http://www.host.test/foo/bar", "A=a D=d"}, 540 }, 541 }, 542 { 543 "Creation time determines sorting on same length paths.", 544 "http://www.host.test/", 545 []string{ 546 "A=a; path=/foo/bar", 547 "X=x; path=/foo/bar", 548 "Y=y; path=/foo/bar/baz/qux", 549 "B=b; path=/foo/bar/baz/qux", 550 "C=c; path=/foo/bar/baz", 551 "W=w; path=/foo/bar/baz", 552 "Z=z; path=/foo", 553 "D=d; path=/foo"}, 554 "A=a B=b C=c D=d W=w X=x Y=y Z=z", 555 []query{ 556 {"http://www.host.test/foo/bar/baz/qux", "Y=y B=b C=c W=w A=a X=x Z=z D=d"}, 557 {"http://www.host.test/foo/bar/baz/", "C=c W=w A=a X=x Z=z D=d"}, 558 {"http://www.host.test/foo/bar", "A=a X=x Z=z D=d"}, 559 }, 560 }, 561 { 562 "Sorting of same-name cookies.", 563 "http://www.host.test/", 564 []string{ 565 "A=1; path=/", 566 "A=2; path=/path", 567 "A=3; path=/quux", 568 "A=4; path=/path/foo", 569 "A=5; domain=.host.test; path=/path", 570 "A=6; domain=.host.test; path=/quux", 571 "A=7; domain=.host.test; path=/path/foo", 572 }, 573 "A=1 A=2 A=3 A=4 A=5 A=6 A=7", 574 []query{ 575 {"http://www.host.test/path", "A=2 A=5 A=1"}, 576 {"http://www.host.test/path/foo", "A=4 A=7 A=2 A=5 A=1"}, 577 }, 578 }, 579 { 580 "Disallow domain cookie on public suffix.", 581 "http://www.bbc.co.uk", 582 []string{ 583 "a=1", 584 "b=2; domain=co.uk", 585 }, 586 "a=1", 587 []query{{"http://www.bbc.co.uk", "a=1"}}, 588 }, 589 { 590 "Host cookie on IP.", 591 "http://192.168.0.10", 592 []string{"a=1"}, 593 "a=1", 594 []query{{"http://192.168.0.10", "a=1"}}, 595 }, 596 { 597 "Domain cookies on IP.", 598 "http://192.168.0.10", 599 []string{ 600 "a=1; domain=192.168.0.10", // allowed 601 "b=2; domain=172.31.9.9", // rejected, can't set cookie for other IP 602 "c=3; domain=.192.168.0.10", // rejected like in most browsers 603 }, 604 "a=1", 605 []query{ 606 {"http://192.168.0.10", "a=1"}, 607 {"http://172.31.9.9", ""}, 608 {"http://www.fancy.192.168.0.10", ""}, 609 }, 610 }, 611 { 612 "Port is ignored #1.", 613 "http://www.host.test/", 614 []string{"a=1"}, 615 "a=1", 616 []query{ 617 {"http://www.host.test", "a=1"}, 618 {"http://www.host.test:8080/", "a=1"}, 619 }, 620 }, 621 { 622 "Port is ignored #2.", 623 "http://www.host.test:8080/", 624 []string{"a=1"}, 625 "a=1", 626 []query{ 627 {"http://www.host.test", "a=1"}, 628 {"http://www.host.test:8080/", "a=1"}, 629 {"http://www.host.test:1234/", "a=1"}, 630 }, 631 }, 632 } 633 634 func TestBasics(t *testing.T) { 635 for _, test := range basicsTests { 636 jar := newTestJar() 637 test.run(t, jar) 638 } 639 } 640 641 // updateAndDeleteTests contains jarTests which must be performed on the same 642 // Jar. 643 var updateAndDeleteTests = [...]jarTest{ 644 { 645 "Set initial cookies.", 646 "http://www.host.test", 647 []string{ 648 "a=1", 649 "b=2; secure", 650 "c=3; httponly", 651 "d=4; secure; httponly"}, 652 "a=1 b=2 c=3 d=4", 653 []query{ 654 {"http://www.host.test", "a=1 c=3"}, 655 {"https://www.host.test", "a=1 b=2 c=3 d=4"}, 656 }, 657 }, 658 { 659 "Update value via http.", 660 "http://www.host.test", 661 []string{ 662 "a=w", 663 "b=x; secure", 664 "c=y; httponly", 665 "d=z; secure; httponly"}, 666 "a=w b=x c=y d=z", 667 []query{ 668 {"http://www.host.test", "a=w c=y"}, 669 {"https://www.host.test", "a=w b=x c=y d=z"}, 670 }, 671 }, 672 { 673 "Clear Secure flag from an http.", 674 "http://www.host.test/", 675 []string{ 676 "b=xx", 677 "d=zz; httponly"}, 678 "a=w b=xx c=y d=zz", 679 []query{{"http://www.host.test", "a=w b=xx c=y d=zz"}}, 680 }, 681 { 682 "Delete all.", 683 "http://www.host.test/", 684 []string{ 685 "a=1; max-Age=-1", // delete via MaxAge 686 "b=2; " + expiresIn(-10), // delete via Expires 687 "c=2; max-age=-1; " + expiresIn(-10), // delete via both 688 "d=4; max-age=-1; " + expiresIn(10)}, // MaxAge takes precedence 689 "", 690 []query{{"http://www.host.test", ""}}, 691 }, 692 { 693 "Refill #1.", 694 "http://www.host.test", 695 []string{ 696 "A=1", 697 "A=2; path=/foo", 698 "A=3; domain=.host.test", 699 "A=4; path=/foo; domain=.host.test"}, 700 "A=1 A=2 A=3 A=4", 701 []query{{"http://www.host.test/foo", "A=2 A=4 A=1 A=3"}}, 702 }, 703 { 704 "Refill #2.", 705 "http://www.google.com", 706 []string{ 707 "A=6", 708 "A=7; path=/foo", 709 "A=8; domain=.google.com", 710 "A=9; path=/foo; domain=.google.com"}, 711 "A=1 A=2 A=3 A=4 A=6 A=7 A=8 A=9", 712 []query{ 713 {"http://www.host.test/foo", "A=2 A=4 A=1 A=3"}, 714 {"http://www.google.com/foo", "A=7 A=9 A=6 A=8"}, 715 }, 716 }, 717 { 718 "Delete A7.", 719 "http://www.google.com", 720 []string{"A=; path=/foo; max-age=-1"}, 721 "A=1 A=2 A=3 A=4 A=6 A=8 A=9", 722 []query{ 723 {"http://www.host.test/foo", "A=2 A=4 A=1 A=3"}, 724 {"http://www.google.com/foo", "A=9 A=6 A=8"}, 725 }, 726 }, 727 { 728 "Delete A4.", 729 "http://www.host.test", 730 []string{"A=; path=/foo; domain=host.test; max-age=-1"}, 731 "A=1 A=2 A=3 A=6 A=8 A=9", 732 []query{ 733 {"http://www.host.test/foo", "A=2 A=1 A=3"}, 734 {"http://www.google.com/foo", "A=9 A=6 A=8"}, 735 }, 736 }, 737 { 738 "Delete A6.", 739 "http://www.google.com", 740 []string{"A=; max-age=-1"}, 741 "A=1 A=2 A=3 A=8 A=9", 742 []query{ 743 {"http://www.host.test/foo", "A=2 A=1 A=3"}, 744 {"http://www.google.com/foo", "A=9 A=8"}, 745 }, 746 }, 747 { 748 "Delete A3.", 749 "http://www.host.test", 750 []string{"A=; domain=host.test; max-age=-1"}, 751 "A=1 A=2 A=8 A=9", 752 []query{ 753 {"http://www.host.test/foo", "A=2 A=1"}, 754 {"http://www.google.com/foo", "A=9 A=8"}, 755 }, 756 }, 757 { 758 "No cross-domain delete.", 759 "http://www.host.test", 760 []string{ 761 "A=; domain=google.com; max-age=-1", 762 "A=; path=/foo; domain=google.com; max-age=-1"}, 763 "A=1 A=2 A=8 A=9", 764 []query{ 765 {"http://www.host.test/foo", "A=2 A=1"}, 766 {"http://www.google.com/foo", "A=9 A=8"}, 767 }, 768 }, 769 { 770 "Delete A8 and A9.", 771 "http://www.google.com", 772 []string{ 773 "A=; domain=google.com; max-age=-1", 774 "A=; path=/foo; domain=google.com; max-age=-1"}, 775 "A=1 A=2", 776 []query{ 777 {"http://www.host.test/foo", "A=2 A=1"}, 778 {"http://www.google.com/foo", ""}, 779 }, 780 }, 781 } 782 783 func TestUpdateAndDelete(t *testing.T) { 784 jar := newTestJar() 785 for _, test := range updateAndDeleteTests { 786 test.run(t, jar) 787 } 788 } 789 790 func TestExpiration(t *testing.T) { 791 jar := newTestJar() 792 jarTest{ 793 "Expiration.", 794 "http://www.host.test", 795 []string{ 796 "a=1", 797 "b=2; max-age=3", 798 "c=3; " + expiresIn(3), 799 "d=4; max-age=5", 800 "e=5; " + expiresIn(5), 801 "f=6; max-age=100", 802 }, 803 "a=1 b=2 c=3 d=4 e=5 f=6", // executed at t0 + 1001 ms 804 []query{ 805 {"http://www.host.test", "a=1 b=2 c=3 d=4 e=5 f=6"}, // t0 + 2002 ms 806 {"http://www.host.test", "a=1 d=4 e=5 f=6"}, // t0 + 3003 ms 807 {"http://www.host.test", "a=1 d=4 e=5 f=6"}, // t0 + 4004 ms 808 {"http://www.host.test", "a=1 f=6"}, // t0 + 5005 ms 809 {"http://www.host.test", "a=1 f=6"}, // t0 + 6006 ms 810 }, 811 }.run(t, jar) 812 } 813 814 // 815 // Tests derived from Chromium's cookie_store_unittest.h. 816 // 817 818 // See http://src.chromium.org/viewvc/chrome/trunk/src/net/cookies/cookie_store_unittest.h?revision=159685&content-type=text/plain 819 // Some of the original tests are in a bad condition (e.g. 820 // DomainWithTrailingDotTest) or are not RFC 6265 conforming (e.g. 821 // TestNonDottedAndTLD #1 and #6) and have not been ported. 822 823 // chromiumBasicsTests contains fundamental tests. Each jarTest has to be 824 // performed on a fresh, empty Jar. 825 var chromiumBasicsTests = [...]jarTest{ 826 { 827 "DomainWithTrailingDotTest.", 828 "http://www.google.com/", 829 []string{ 830 "a=1; domain=.www.google.com.", 831 "b=2; domain=.www.google.com.."}, 832 "", 833 []query{ 834 {"http://www.google.com", ""}, 835 }, 836 }, 837 { 838 "ValidSubdomainTest #1.", 839 "http://a.b.c.d.com", 840 []string{ 841 "a=1; domain=.a.b.c.d.com", 842 "b=2; domain=.b.c.d.com", 843 "c=3; domain=.c.d.com", 844 "d=4; domain=.d.com"}, 845 "a=1 b=2 c=3 d=4", 846 []query{ 847 {"http://a.b.c.d.com", "a=1 b=2 c=3 d=4"}, 848 {"http://b.c.d.com", "b=2 c=3 d=4"}, 849 {"http://c.d.com", "c=3 d=4"}, 850 {"http://d.com", "d=4"}, 851 }, 852 }, 853 { 854 "ValidSubdomainTest #2.", 855 "http://a.b.c.d.com", 856 []string{ 857 "a=1; domain=.a.b.c.d.com", 858 "b=2; domain=.b.c.d.com", 859 "c=3; domain=.c.d.com", 860 "d=4; domain=.d.com", 861 "X=bcd; domain=.b.c.d.com", 862 "X=cd; domain=.c.d.com"}, 863 "X=bcd X=cd a=1 b=2 c=3 d=4", 864 []query{ 865 {"http://b.c.d.com", "b=2 c=3 d=4 X=bcd X=cd"}, 866 {"http://c.d.com", "c=3 d=4 X=cd"}, 867 }, 868 }, 869 { 870 "InvalidDomainTest #1.", 871 "http://foo.bar.com", 872 []string{ 873 "a=1; domain=.yo.foo.bar.com", 874 "b=2; domain=.foo.com", 875 "c=3; domain=.bar.foo.com", 876 "d=4; domain=.foo.bar.com.net", 877 "e=5; domain=ar.com", 878 "f=6; domain=.", 879 "g=7; domain=/", 880 "h=8; domain=http://foo.bar.com", 881 "i=9; domain=..foo.bar.com", 882 "j=10; domain=..bar.com", 883 "k=11; domain=.foo.bar.com?blah", 884 "l=12; domain=.foo.bar.com/blah", 885 "m=12; domain=.foo.bar.com:80", 886 "n=14; domain=.foo.bar.com:", 887 "o=15; domain=.foo.bar.com#sup", 888 }, 889 "", // Jar is empty. 890 []query{{"http://foo.bar.com", ""}}, 891 }, 892 { 893 "InvalidDomainTest #2.", 894 "http://foo.com.com", 895 []string{"a=1; domain=.foo.com.com.com"}, 896 "", 897 []query{{"http://foo.bar.com", ""}}, 898 }, 899 { 900 "DomainWithoutLeadingDotTest #1.", 901 "http://manage.hosted.filefront.com", 902 []string{"a=1; domain=filefront.com"}, 903 "a=1", 904 []query{{"http://www.filefront.com", "a=1"}}, 905 }, 906 { 907 "DomainWithoutLeadingDotTest #2.", 908 "http://www.google.com", 909 []string{"a=1; domain=www.google.com"}, 910 "a=1", 911 []query{ 912 {"http://www.google.com", "a=1"}, 913 {"http://sub.www.google.com", "a=1"}, 914 {"http://something-else.com", ""}, 915 }, 916 }, 917 { 918 "CaseInsensitiveDomainTest.", 919 "http://www.google.com", 920 []string{ 921 "a=1; domain=.GOOGLE.COM", 922 "b=2; domain=.www.gOOgLE.coM"}, 923 "a=1 b=2", 924 []query{{"http://www.google.com", "a=1 b=2"}}, 925 }, 926 { 927 "TestIpAddress #1.", 928 "http://1.2.3.4/foo", 929 []string{"a=1; path=/"}, 930 "a=1", 931 []query{{"http://1.2.3.4/foo", "a=1"}}, 932 }, 933 { 934 "TestIpAddress #2.", 935 "http://1.2.3.4/foo", 936 []string{ 937 "a=1; domain=.1.2.3.4", 938 "b=2; domain=.3.4"}, 939 "", 940 []query{{"http://1.2.3.4/foo", ""}}, 941 }, 942 { 943 "TestIpAddress #3.", 944 "http://1.2.3.4/foo", 945 []string{"a=1; domain=1.2.3.3"}, 946 "", 947 []query{{"http://1.2.3.4/foo", ""}}, 948 }, 949 { 950 "TestIpAddress #4.", 951 "http://1.2.3.4/foo", 952 []string{"a=1; domain=1.2.3.4"}, 953 "a=1", 954 []query{{"http://1.2.3.4/foo", "a=1"}}, 955 }, 956 { 957 "TestNonDottedAndTLD #2.", 958 "http://com./index.html", 959 []string{"a=1"}, 960 "a=1", 961 []query{ 962 {"http://com./index.html", "a=1"}, 963 {"http://no-cookies.com./index.html", ""}, 964 }, 965 }, 966 { 967 "TestNonDottedAndTLD #3.", 968 "http://a.b", 969 []string{ 970 "a=1; domain=.b", 971 "b=2; domain=b"}, 972 "", 973 []query{{"http://bar.foo", ""}}, 974 }, 975 { 976 "TestNonDottedAndTLD #4.", 977 "http://google.com", 978 []string{ 979 "a=1; domain=.com", 980 "b=2; domain=com"}, 981 "", 982 []query{{"http://google.com", ""}}, 983 }, 984 { 985 "TestNonDottedAndTLD #5.", 986 "http://google.co.uk", 987 []string{ 988 "a=1; domain=.co.uk", 989 "b=2; domain=.uk"}, 990 "", 991 []query{ 992 {"http://google.co.uk", ""}, 993 {"http://else.co.com", ""}, 994 {"http://else.uk", ""}, 995 }, 996 }, 997 { 998 "TestHostEndsWithDot.", 999 "http://www.google.com", 1000 []string{ 1001 "a=1", 1002 "b=2; domain=.www.google.com."}, 1003 "a=1", 1004 []query{{"http://www.google.com", "a=1"}}, 1005 }, 1006 { 1007 "PathTest", 1008 "http://www.google.izzle", 1009 []string{"a=1; path=/wee"}, 1010 "a=1", 1011 []query{ 1012 {"http://www.google.izzle/wee", "a=1"}, 1013 {"http://www.google.izzle/wee/", "a=1"}, 1014 {"http://www.google.izzle/wee/war", "a=1"}, 1015 {"http://www.google.izzle/wee/war/more/more", "a=1"}, 1016 {"http://www.google.izzle/weehee", ""}, 1017 {"http://www.google.izzle/", ""}, 1018 }, 1019 }, 1020 } 1021 1022 func TestChromiumBasics(t *testing.T) { 1023 for _, test := range chromiumBasicsTests { 1024 jar := newTestJar() 1025 test.run(t, jar) 1026 } 1027 } 1028 1029 // chromiumDomainTests contains jarTests which must be executed all on the 1030 // same Jar. 1031 var chromiumDomainTests = [...]jarTest{ 1032 { 1033 "Fill #1.", 1034 "http://www.google.izzle", 1035 []string{"A=B"}, 1036 "A=B", 1037 []query{{"http://www.google.izzle", "A=B"}}, 1038 }, 1039 { 1040 "Fill #2.", 1041 "http://www.google.izzle", 1042 []string{"C=D; domain=.google.izzle"}, 1043 "A=B C=D", 1044 []query{{"http://www.google.izzle", "A=B C=D"}}, 1045 }, 1046 { 1047 "Verify A is a host cookie and not accessible from subdomain.", 1048 "http://unused.nil", 1049 []string{}, 1050 "A=B C=D", 1051 []query{{"http://foo.www.google.izzle", "C=D"}}, 1052 }, 1053 { 1054 "Verify domain cookies are found on proper domain.", 1055 "http://www.google.izzle", 1056 []string{"E=F; domain=.www.google.izzle"}, 1057 "A=B C=D E=F", 1058 []query{{"http://www.google.izzle", "A=B C=D E=F"}}, 1059 }, 1060 { 1061 "Leading dots in domain attributes are optional.", 1062 "http://www.google.izzle", 1063 []string{"G=H; domain=www.google.izzle"}, 1064 "A=B C=D E=F G=H", 1065 []query{{"http://www.google.izzle", "A=B C=D E=F G=H"}}, 1066 }, 1067 { 1068 "Verify domain enforcement works #1.", 1069 "http://www.google.izzle", 1070 []string{"K=L; domain=.bar.www.google.izzle"}, 1071 "A=B C=D E=F G=H", 1072 []query{{"http://bar.www.google.izzle", "C=D E=F G=H"}}, 1073 }, 1074 { 1075 "Verify domain enforcement works #2.", 1076 "http://unused.nil", 1077 []string{}, 1078 "A=B C=D E=F G=H", 1079 []query{{"http://www.google.izzle", "A=B C=D E=F G=H"}}, 1080 }, 1081 } 1082 1083 func TestChromiumDomain(t *testing.T) { 1084 jar := newTestJar() 1085 for _, test := range chromiumDomainTests { 1086 test.run(t, jar) 1087 } 1088 1089 } 1090 1091 // chromiumDeletionTests must be performed all on the same Jar. 1092 var chromiumDeletionTests = [...]jarTest{ 1093 { 1094 "Create session cookie a1.", 1095 "http://www.google.com", 1096 []string{"a=1"}, 1097 "a=1", 1098 []query{{"http://www.google.com", "a=1"}}, 1099 }, 1100 { 1101 "Delete sc a1 via MaxAge.", 1102 "http://www.google.com", 1103 []string{"a=1; max-age=-1"}, 1104 "", 1105 []query{{"http://www.google.com", ""}}, 1106 }, 1107 { 1108 "Create session cookie b2.", 1109 "http://www.google.com", 1110 []string{"b=2"}, 1111 "b=2", 1112 []query{{"http://www.google.com", "b=2"}}, 1113 }, 1114 { 1115 "Delete sc b2 via Expires.", 1116 "http://www.google.com", 1117 []string{"b=2; " + expiresIn(-10)}, 1118 "", 1119 []query{{"http://www.google.com", ""}}, 1120 }, 1121 { 1122 "Create persistent cookie c3.", 1123 "http://www.google.com", 1124 []string{"c=3; max-age=3600"}, 1125 "c=3", 1126 []query{{"http://www.google.com", "c=3"}}, 1127 }, 1128 { 1129 "Delete pc c3 via MaxAge.", 1130 "http://www.google.com", 1131 []string{"c=3; max-age=-1"}, 1132 "", 1133 []query{{"http://www.google.com", ""}}, 1134 }, 1135 { 1136 "Create persistent cookie d4.", 1137 "http://www.google.com", 1138 []string{"d=4; max-age=3600"}, 1139 "d=4", 1140 []query{{"http://www.google.com", "d=4"}}, 1141 }, 1142 { 1143 "Delete pc d4 via Expires.", 1144 "http://www.google.com", 1145 []string{"d=4; " + expiresIn(-10)}, 1146 "", 1147 []query{{"http://www.google.com", ""}}, 1148 }, 1149 } 1150 1151 func TestChromiumDeletion(t *testing.T) { 1152 jar := newTestJar() 1153 for _, test := range chromiumDeletionTests { 1154 test.run(t, jar) 1155 } 1156 } 1157 1158 // domainHandlingTests tests and documents the rules for domain handling. 1159 // Each test must be performed on an empty new Jar. 1160 var domainHandlingTests = [...]jarTest{ 1161 { 1162 "Host cookie", 1163 "http://www.host.test", 1164 []string{"a=1"}, 1165 "a=1", 1166 []query{ 1167 {"http://www.host.test", "a=1"}, 1168 {"http://host.test", ""}, 1169 {"http://bar.host.test", ""}, 1170 {"http://foo.www.host.test", ""}, 1171 {"http://other.test", ""}, 1172 {"http://test", ""}, 1173 }, 1174 }, 1175 { 1176 "Domain cookie #1", 1177 "http://www.host.test", 1178 []string{"a=1; domain=host.test"}, 1179 "a=1", 1180 []query{ 1181 {"http://www.host.test", "a=1"}, 1182 {"http://host.test", "a=1"}, 1183 {"http://bar.host.test", "a=1"}, 1184 {"http://foo.www.host.test", "a=1"}, 1185 {"http://other.test", ""}, 1186 {"http://test", ""}, 1187 }, 1188 }, 1189 { 1190 "Domain cookie #2", 1191 "http://www.host.test", 1192 []string{"a=1; domain=.host.test"}, 1193 "a=1", 1194 []query{ 1195 {"http://www.host.test", "a=1"}, 1196 {"http://host.test", "a=1"}, 1197 {"http://bar.host.test", "a=1"}, 1198 {"http://foo.www.host.test", "a=1"}, 1199 {"http://other.test", ""}, 1200 {"http://test", ""}, 1201 }, 1202 }, 1203 { 1204 "Host cookie on IDNA domain #1", 1205 "http://www.bücher.test", 1206 []string{"a=1"}, 1207 "a=1", 1208 []query{ 1209 {"http://www.bücher.test", "a=1"}, 1210 {"http://www.xn--bcher-kva.test", "a=1"}, 1211 {"http://bücher.test", ""}, 1212 {"http://xn--bcher-kva.test", ""}, 1213 {"http://bar.bücher.test", ""}, 1214 {"http://bar.xn--bcher-kva.test", ""}, 1215 {"http://foo.www.bücher.test", ""}, 1216 {"http://foo.www.xn--bcher-kva.test", ""}, 1217 {"http://other.test", ""}, 1218 {"http://test", ""}, 1219 }, 1220 }, 1221 { 1222 "Host cookie on IDNA domain #2", 1223 "http://www.xn--bcher-kva.test", 1224 []string{"a=1"}, 1225 "a=1", 1226 []query{ 1227 {"http://www.bücher.test", "a=1"}, 1228 {"http://www.xn--bcher-kva.test", "a=1"}, 1229 {"http://bücher.test", ""}, 1230 {"http://xn--bcher-kva.test", ""}, 1231 {"http://bar.bücher.test", ""}, 1232 {"http://bar.xn--bcher-kva.test", ""}, 1233 {"http://foo.www.bücher.test", ""}, 1234 {"http://foo.www.xn--bcher-kva.test", ""}, 1235 {"http://other.test", ""}, 1236 {"http://test", ""}, 1237 }, 1238 }, 1239 { 1240 "Domain cookie on IDNA domain #1", 1241 "http://www.bücher.test", 1242 []string{"a=1; domain=xn--bcher-kva.test"}, 1243 "a=1", 1244 []query{ 1245 {"http://www.bücher.test", "a=1"}, 1246 {"http://www.xn--bcher-kva.test", "a=1"}, 1247 {"http://bücher.test", "a=1"}, 1248 {"http://xn--bcher-kva.test", "a=1"}, 1249 {"http://bar.bücher.test", "a=1"}, 1250 {"http://bar.xn--bcher-kva.test", "a=1"}, 1251 {"http://foo.www.bücher.test", "a=1"}, 1252 {"http://foo.www.xn--bcher-kva.test", "a=1"}, 1253 {"http://other.test", ""}, 1254 {"http://test", ""}, 1255 }, 1256 }, 1257 { 1258 "Domain cookie on IDNA domain #2", 1259 "http://www.xn--bcher-kva.test", 1260 []string{"a=1; domain=xn--bcher-kva.test"}, 1261 "a=1", 1262 []query{ 1263 {"http://www.bücher.test", "a=1"}, 1264 {"http://www.xn--bcher-kva.test", "a=1"}, 1265 {"http://bücher.test", "a=1"}, 1266 {"http://xn--bcher-kva.test", "a=1"}, 1267 {"http://bar.bücher.test", "a=1"}, 1268 {"http://bar.xn--bcher-kva.test", "a=1"}, 1269 {"http://foo.www.bücher.test", "a=1"}, 1270 {"http://foo.www.xn--bcher-kva.test", "a=1"}, 1271 {"http://other.test", ""}, 1272 {"http://test", ""}, 1273 }, 1274 }, 1275 { 1276 "Host cookie on TLD.", 1277 "http://com", 1278 []string{"a=1"}, 1279 "a=1", 1280 []query{ 1281 {"http://com", "a=1"}, 1282 {"http://any.com", ""}, 1283 {"http://any.test", ""}, 1284 }, 1285 }, 1286 { 1287 "Domain cookie on TLD becomes a host cookie.", 1288 "http://com", 1289 []string{"a=1; domain=com"}, 1290 "a=1", 1291 []query{ 1292 {"http://com", "a=1"}, 1293 {"http://any.com", ""}, 1294 {"http://any.test", ""}, 1295 }, 1296 }, 1297 { 1298 "Host cookie on public suffix.", 1299 "http://co.uk", 1300 []string{"a=1"}, 1301 "a=1", 1302 []query{ 1303 {"http://co.uk", "a=1"}, 1304 {"http://uk", ""}, 1305 {"http://some.co.uk", ""}, 1306 {"http://foo.some.co.uk", ""}, 1307 {"http://any.uk", ""}, 1308 }, 1309 }, 1310 { 1311 "Domain cookie on public suffix is ignored.", 1312 "http://some.co.uk", 1313 []string{"a=1; domain=co.uk"}, 1314 "", 1315 []query{ 1316 {"http://co.uk", ""}, 1317 {"http://uk", ""}, 1318 {"http://some.co.uk", ""}, 1319 {"http://foo.some.co.uk", ""}, 1320 {"http://any.uk", ""}, 1321 }, 1322 }, 1323 } 1324 1325 func TestDomainHandling(t *testing.T) { 1326 for _, test := range domainHandlingTests { 1327 jar := newTestJar() 1328 test.run(t, jar) 1329 } 1330 } 1331 1332 func TestIssue19384(t *testing.T) { 1333 cookies := []*http.Cookie{{Name: "name", Value: "value"}} 1334 for _, host := range []string{"", ".", "..", "..."} { 1335 jar, _ := New(nil) 1336 u := &url.URL{Scheme: "http", Host: host, Path: "/"} 1337 if got := jar.Cookies(u); len(got) != 0 { 1338 t.Errorf("host %q, got %v", host, got) 1339 } 1340 jar.SetCookies(u, cookies) 1341 if got := jar.Cookies(u); len(got) != 1 || got[0].Value != "value" { 1342 t.Errorf("host %q, got %v", host, got) 1343 } 1344 } 1345 }