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