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