1"""Tests for http/cookiejar.py.""" 2 3import os 4import re 5import test.support 6import time 7import unittest 8import urllib.request 9 10from http.cookiejar import (time2isoz, http2time, iso2time, time2netscape, 11 parse_ns_headers, join_header_words, split_header_words, Cookie, 12 CookieJar, DefaultCookiePolicy, LWPCookieJar, MozillaCookieJar, 13 LoadError, lwp_cookie_str, DEFAULT_HTTP_PORT, escape_path, 14 reach, is_HDN, domain_match, user_domain_match, request_path, 15 request_port, request_host) 16 17 18class DateTimeTests(unittest.TestCase): 19 20 def test_time2isoz(self): 21 base = 1019227000 22 day = 24*3600 23 self.assertEqual(time2isoz(base), "2002-04-19 14:36:40Z") 24 self.assertEqual(time2isoz(base+day), "2002-04-20 14:36:40Z") 25 self.assertEqual(time2isoz(base+2*day), "2002-04-21 14:36:40Z") 26 self.assertEqual(time2isoz(base+3*day), "2002-04-22 14:36:40Z") 27 28 az = time2isoz() 29 bz = time2isoz(500000) 30 for text in (az, bz): 31 self.assertRegex(text, r"^\d{4}-\d\d-\d\d \d\d:\d\d:\d\dZ$", 32 "bad time2isoz format: %s %s" % (az, bz)) 33 34 def test_time2netscape(self): 35 base = 1019227000 36 day = 24*3600 37 self.assertEqual(time2netscape(base), "Fri, 19-Apr-2002 14:36:40 GMT") 38 self.assertEqual(time2netscape(base+day), 39 "Sat, 20-Apr-2002 14:36:40 GMT") 40 41 self.assertEqual(time2netscape(base+2*day), 42 "Sun, 21-Apr-2002 14:36:40 GMT") 43 44 self.assertEqual(time2netscape(base+3*day), 45 "Mon, 22-Apr-2002 14:36:40 GMT") 46 47 az = time2netscape() 48 bz = time2netscape(500000) 49 for text in (az, bz): 50 # Format "%s, %02d-%s-%04d %02d:%02d:%02d GMT" 51 self.assertRegex( 52 text, 53 r"[a-zA-Z]{3}, \d{2}-[a-zA-Z]{3}-\d{4} \d{2}:\d{2}:\d{2} GMT$", 54 "bad time2netscape format: %s %s" % (az, bz)) 55 56 def test_http2time(self): 57 def parse_date(text): 58 return time.gmtime(http2time(text))[:6] 59 60 self.assertEqual(parse_date("01 Jan 2001"), (2001, 1, 1, 0, 0, 0.0)) 61 62 # this test will break around year 2070 63 self.assertEqual(parse_date("03-Feb-20"), (2020, 2, 3, 0, 0, 0.0)) 64 65 # this test will break around year 2048 66 self.assertEqual(parse_date("03-Feb-98"), (1998, 2, 3, 0, 0, 0.0)) 67 68 def test_http2time_formats(self): 69 # test http2time for supported dates. Test cases with 2 digit year 70 # will probably break in year 2044. 71 tests = [ 72 'Thu, 03 Feb 1994 00:00:00 GMT', # proposed new HTTP format 73 'Thursday, 03-Feb-94 00:00:00 GMT', # old rfc850 HTTP format 74 'Thursday, 03-Feb-1994 00:00:00 GMT', # broken rfc850 HTTP format 75 76 '03 Feb 1994 00:00:00 GMT', # HTTP format (no weekday) 77 '03-Feb-94 00:00:00 GMT', # old rfc850 (no weekday) 78 '03-Feb-1994 00:00:00 GMT', # broken rfc850 (no weekday) 79 '03-Feb-1994 00:00 GMT', # broken rfc850 (no weekday, no seconds) 80 '03-Feb-1994 00:00', # broken rfc850 (no weekday, no seconds, no tz) 81 '02-Feb-1994 24:00', # broken rfc850 (no weekday, no seconds, 82 # no tz) using hour 24 with yesterday date 83 84 '03-Feb-94', # old rfc850 HTTP format (no weekday, no time) 85 '03-Feb-1994', # broken rfc850 HTTP format (no weekday, no time) 86 '03 Feb 1994', # proposed new HTTP format (no weekday, no time) 87 88 # A few tests with extra space at various places 89 ' 03 Feb 1994 0:00 ', 90 ' 03-Feb-1994 ', 91 ] 92 93 test_t = 760233600 # assume broken POSIX counting of seconds 94 result = time2isoz(test_t) 95 expected = "1994-02-03 00:00:00Z" 96 self.assertEqual(result, expected, 97 "%s => '%s' (%s)" % (test_t, result, expected)) 98 99 for s in tests: 100 self.assertEqual(http2time(s), test_t, s) 101 self.assertEqual(http2time(s.lower()), test_t, s.lower()) 102 self.assertEqual(http2time(s.upper()), test_t, s.upper()) 103 104 def test_http2time_garbage(self): 105 for test in [ 106 '', 107 'Garbage', 108 'Mandag 16. September 1996', 109 '01-00-1980', 110 '01-13-1980', 111 '00-01-1980', 112 '32-01-1980', 113 '01-01-1980 25:00:00', 114 '01-01-1980 00:61:00', 115 '01-01-1980 00:00:62', 116 '08-Oct-3697739', 117 '08-01-3697739', 118 '09 Feb 19942632 22:23:32 GMT', 119 'Wed, 09 Feb 1994834 22:23:32 GMT', 120 ]: 121 self.assertIsNone(http2time(test), 122 "http2time(%s) is not None\n" 123 "http2time(test) %s" % (test, http2time(test))) 124 125 def test_iso2time(self): 126 def parse_date(text): 127 return time.gmtime(iso2time(text))[:6] 128 129 # ISO 8601 compact format 130 self.assertEqual(parse_date("19940203T141529Z"), 131 (1994, 2, 3, 14, 15, 29)) 132 133 # ISO 8601 with time behind UTC 134 self.assertEqual(parse_date("1994-02-03 07:15:29 -0700"), 135 (1994, 2, 3, 14, 15, 29)) 136 137 # ISO 8601 with time ahead of UTC 138 self.assertEqual(parse_date("1994-02-03 19:45:29 +0530"), 139 (1994, 2, 3, 14, 15, 29)) 140 141 def test_iso2time_formats(self): 142 # test iso2time for supported dates. 143 tests = [ 144 '1994-02-03 00:00:00 -0000', # ISO 8601 format 145 '1994-02-03 00:00:00 +0000', # ISO 8601 format 146 '1994-02-03 00:00:00', # zone is optional 147 '1994-02-03', # only date 148 '1994-02-03T00:00:00', # Use T as separator 149 '19940203', # only date 150 '1994-02-02 24:00:00', # using hour-24 yesterday date 151 '19940203T000000Z', # ISO 8601 compact format 152 153 # A few tests with extra space at various places 154 ' 1994-02-03 ', 155 ' 1994-02-03T00:00:00 ', 156 ] 157 158 test_t = 760233600 # assume broken POSIX counting of seconds 159 for s in tests: 160 self.assertEqual(iso2time(s), test_t, s) 161 self.assertEqual(iso2time(s.lower()), test_t, s.lower()) 162 self.assertEqual(iso2time(s.upper()), test_t, s.upper()) 163 164 def test_iso2time_garbage(self): 165 for test in [ 166 '', 167 'Garbage', 168 'Thursday, 03-Feb-94 00:00:00 GMT', 169 '1980-00-01', 170 '1980-13-01', 171 '1980-01-00', 172 '1980-01-32', 173 '1980-01-01 25:00:00', 174 '1980-01-01 00:61:00', 175 '01-01-1980 00:00:62', 176 '01-01-1980T00:00:62', 177 '19800101T250000Z' 178 '1980-01-01 00:00:00 -2500', 179 ]: 180 self.assertIsNone(iso2time(test), 181 "iso2time(%s) is not None\n" 182 "iso2time(test) %s" % (test, iso2time(test))) 183 184 185class HeaderTests(unittest.TestCase): 186 187 def test_parse_ns_headers(self): 188 # quotes should be stripped 189 expected = [[('foo', 'bar'), ('expires', 2209069412), ('version', '0')]] 190 for hdr in [ 191 'foo=bar; expires=01 Jan 2040 22:23:32 GMT', 192 'foo=bar; expires="01 Jan 2040 22:23:32 GMT"', 193 ]: 194 self.assertEqual(parse_ns_headers([hdr]), expected) 195 196 def test_parse_ns_headers_version(self): 197 198 # quotes should be stripped 199 expected = [[('foo', 'bar'), ('version', '1')]] 200 for hdr in [ 201 'foo=bar; version="1"', 202 'foo=bar; Version="1"', 203 ]: 204 self.assertEqual(parse_ns_headers([hdr]), expected) 205 206 def test_parse_ns_headers_special_names(self): 207 # names such as 'expires' are not special in first name=value pair 208 # of Set-Cookie: header 209 # Cookie with name 'expires' 210 hdr = 'expires=01 Jan 2040 22:23:32 GMT' 211 expected = [[("expires", "01 Jan 2040 22:23:32 GMT"), ("version", "0")]] 212 self.assertEqual(parse_ns_headers([hdr]), expected) 213 214 def test_join_header_words(self): 215 joined = join_header_words([[("foo", None), ("bar", "baz")]]) 216 self.assertEqual(joined, "foo; bar=baz") 217 218 self.assertEqual(join_header_words([[]]), "") 219 220 def test_split_header_words(self): 221 tests = [ 222 ("foo", [[("foo", None)]]), 223 ("foo=bar", [[("foo", "bar")]]), 224 (" foo ", [[("foo", None)]]), 225 (" foo= ", [[("foo", "")]]), 226 (" foo=", [[("foo", "")]]), 227 (" foo= ; ", [[("foo", "")]]), 228 (" foo= ; bar= baz ", [[("foo", ""), ("bar", "baz")]]), 229 ("foo=bar bar=baz", [[("foo", "bar"), ("bar", "baz")]]), 230 # doesn't really matter if this next fails, but it works ATM 231 ("foo= bar=baz", [[("foo", "bar=baz")]]), 232 ("foo=bar;bar=baz", [[("foo", "bar"), ("bar", "baz")]]), 233 ('foo bar baz', [[("foo", None), ("bar", None), ("baz", None)]]), 234 ("a, b, c", [[("a", None)], [("b", None)], [("c", None)]]), 235 (r'foo; bar=baz, spam=, foo="\,\;\"", bar= ', 236 [[("foo", None), ("bar", "baz")], 237 [("spam", "")], [("foo", ',;"')], [("bar", "")]]), 238 ] 239 240 for arg, expect in tests: 241 try: 242 result = split_header_words([arg]) 243 except: 244 import traceback, io 245 f = io.StringIO() 246 traceback.print_exc(None, f) 247 result = "(error -- traceback follows)\n\n%s" % f.getvalue() 248 self.assertEqual(result, expect, """ 249When parsing: '%s' 250Expected: '%s' 251Got: '%s' 252""" % (arg, expect, result)) 253 254 def test_roundtrip(self): 255 tests = [ 256 ("foo", "foo"), 257 ("foo=bar", "foo=bar"), 258 (" foo ", "foo"), 259 ("foo=", 'foo=""'), 260 ("foo=bar bar=baz", "foo=bar; bar=baz"), 261 ("foo=bar;bar=baz", "foo=bar; bar=baz"), 262 ('foo bar baz', "foo; bar; baz"), 263 (r'foo="\"" bar="\\"', r'foo="\""; bar="\\"'), 264 ('foo,,,bar', 'foo, bar'), 265 ('foo=bar,bar=baz', 'foo=bar, bar=baz'), 266 267 ('text/html; charset=iso-8859-1', 268 'text/html; charset="iso-8859-1"'), 269 270 ('foo="bar"; port="80,81"; discard, bar=baz', 271 'foo=bar; port="80,81"; discard, bar=baz'), 272 273 (r'Basic realm="\"foo\\\\bar\""', 274 r'Basic; realm="\"foo\\\\bar\""') 275 ] 276 277 for arg, expect in tests: 278 input = split_header_words([arg]) 279 res = join_header_words(input) 280 self.assertEqual(res, expect, """ 281When parsing: '%s' 282Expected: '%s' 283Got: '%s' 284Input was: '%s' 285""" % (arg, expect, res, input)) 286 287 288class FakeResponse: 289 def __init__(self, headers=[], url=None): 290 """ 291 headers: list of RFC822-style 'Key: value' strings 292 """ 293 import email 294 self._headers = email.message_from_string("\n".join(headers)) 295 self._url = url 296 def info(self): return self._headers 297 298def interact_2965(cookiejar, url, *set_cookie_hdrs): 299 return _interact(cookiejar, url, set_cookie_hdrs, "Set-Cookie2") 300 301def interact_netscape(cookiejar, url, *set_cookie_hdrs): 302 return _interact(cookiejar, url, set_cookie_hdrs, "Set-Cookie") 303 304def _interact(cookiejar, url, set_cookie_hdrs, hdr_name): 305 """Perform a single request / response cycle, returning Cookie: header.""" 306 req = urllib.request.Request(url) 307 cookiejar.add_cookie_header(req) 308 cookie_hdr = req.get_header("Cookie", "") 309 headers = [] 310 for hdr in set_cookie_hdrs: 311 headers.append("%s: %s" % (hdr_name, hdr)) 312 res = FakeResponse(headers, url) 313 cookiejar.extract_cookies(res, req) 314 return cookie_hdr 315 316 317class FileCookieJarTests(unittest.TestCase): 318 def test_lwp_valueless_cookie(self): 319 # cookies with no value should be saved and loaded consistently 320 filename = test.support.TESTFN 321 c = LWPCookieJar() 322 interact_netscape(c, "http://www.acme.com/", 'boo') 323 self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None) 324 try: 325 c.save(filename, ignore_discard=True) 326 c = LWPCookieJar() 327 c.load(filename, ignore_discard=True) 328 finally: 329 try: os.unlink(filename) 330 except OSError: pass 331 self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None) 332 333 def test_bad_magic(self): 334 # OSErrors (eg. file doesn't exist) are allowed to propagate 335 filename = test.support.TESTFN 336 for cookiejar_class in LWPCookieJar, MozillaCookieJar: 337 c = cookiejar_class() 338 try: 339 c.load(filename="for this test to work, a file with this " 340 "filename should not exist") 341 except OSError as exc: 342 # an OSError subclass (likely FileNotFoundError), but not 343 # LoadError 344 self.assertIsNot(exc.__class__, LoadError) 345 else: 346 self.fail("expected OSError for invalid filename") 347 # Invalid contents of cookies file (eg. bad magic string) 348 # causes a LoadError. 349 try: 350 with open(filename, "w") as f: 351 f.write("oops\n") 352 for cookiejar_class in LWPCookieJar, MozillaCookieJar: 353 c = cookiejar_class() 354 self.assertRaises(LoadError, c.load, filename) 355 finally: 356 try: os.unlink(filename) 357 except OSError: pass 358 359class CookieTests(unittest.TestCase): 360 # XXX 361 # Get rid of string comparisons where not actually testing str / repr. 362 # .clear() etc. 363 # IP addresses like 50 (single number, no dot) and domain-matching 364 # functions (and is_HDN)? See draft RFC 2965 errata. 365 # Strictness switches 366 # is_third_party() 367 # unverifiability / third-party blocking 368 # Netscape cookies work the same as RFC 2965 with regard to port. 369 # Set-Cookie with negative max age. 370 # If turn RFC 2965 handling off, Set-Cookie2 cookies should not clobber 371 # Set-Cookie cookies. 372 # Cookie2 should be sent if *any* cookies are not V1 (ie. V0 OR V2 etc.). 373 # Cookies (V1 and V0) with no expiry date should be set to be discarded. 374 # RFC 2965 Quoting: 375 # Should accept unquoted cookie-attribute values? check errata draft. 376 # Which are required on the way in and out? 377 # Should always return quoted cookie-attribute values? 378 # Proper testing of when RFC 2965 clobbers Netscape (waiting for errata). 379 # Path-match on return (same for V0 and V1). 380 # RFC 2965 acceptance and returning rules 381 # Set-Cookie2 without version attribute is rejected. 382 383 # Netscape peculiarities list from Ronald Tschalar. 384 # The first two still need tests, the rest are covered. 385## - Quoting: only quotes around the expires value are recognized as such 386## (and yes, some folks quote the expires value); quotes around any other 387## value are treated as part of the value. 388## - White space: white space around names and values is ignored 389## - Default path: if no path parameter is given, the path defaults to the 390## path in the request-uri up to, but not including, the last '/'. Note 391## that this is entirely different from what the spec says. 392## - Commas and other delimiters: Netscape just parses until the next ';'. 393## This means it will allow commas etc inside values (and yes, both 394## commas and equals are commonly appear in the cookie value). This also 395## means that if you fold multiple Set-Cookie header fields into one, 396## comma-separated list, it'll be a headache to parse (at least my head 397## starts hurting every time I think of that code). 398## - Expires: You'll get all sorts of date formats in the expires, 399## including empty expires attributes ("expires="). Be as flexible as you 400## can, and certainly don't expect the weekday to be there; if you can't 401## parse it, just ignore it and pretend it's a session cookie. 402## - Domain-matching: Netscape uses the 2-dot rule for _all_ domains, not 403## just the 7 special TLD's listed in their spec. And folks rely on 404## that... 405 406 def test_domain_return_ok(self): 407 # test optimization: .domain_return_ok() should filter out most 408 # domains in the CookieJar before we try to access them (because that 409 # may require disk access -- in particular, with MSIECookieJar) 410 # This is only a rough check for performance reasons, so it's not too 411 # critical as long as it's sufficiently liberal. 412 pol = DefaultCookiePolicy() 413 for url, domain, ok in [ 414 ("http://foo.bar.com/", "blah.com", False), 415 ("http://foo.bar.com/", "rhubarb.blah.com", False), 416 ("http://foo.bar.com/", "rhubarb.foo.bar.com", False), 417 ("http://foo.bar.com/", ".foo.bar.com", True), 418 ("http://foo.bar.com/", "foo.bar.com", True), 419 ("http://foo.bar.com/", ".bar.com", True), 420 ("http://foo.bar.com/", "com", True), 421 ("http://foo.com/", "rhubarb.foo.com", False), 422 ("http://foo.com/", ".foo.com", True), 423 ("http://foo.com/", "foo.com", True), 424 ("http://foo.com/", "com", True), 425 ("http://foo/", "rhubarb.foo", False), 426 ("http://foo/", ".foo", True), 427 ("http://foo/", "foo", True), 428 ("http://foo/", "foo.local", True), 429 ("http://foo/", ".local", True), 430 ]: 431 request = urllib.request.Request(url) 432 r = pol.domain_return_ok(domain, request) 433 if ok: self.assertTrue(r) 434 else: self.assertFalse(r) 435 436 def test_missing_value(self): 437 # missing = sign in Cookie: header is regarded by Mozilla as a missing 438 # name, and by http.cookiejar as a missing value 439 filename = test.support.TESTFN 440 c = MozillaCookieJar(filename) 441 interact_netscape(c, "http://www.acme.com/", 'eggs') 442 interact_netscape(c, "http://www.acme.com/", '"spam"; path=/foo/') 443 cookie = c._cookies["www.acme.com"]["/"]["eggs"] 444 self.assertIsNone(cookie.value) 445 self.assertEqual(cookie.name, "eggs") 446 cookie = c._cookies["www.acme.com"]['/foo/']['"spam"'] 447 self.assertIsNone(cookie.value) 448 self.assertEqual(cookie.name, '"spam"') 449 self.assertEqual(lwp_cookie_str(cookie), ( 450 r'"spam"; path="/foo/"; domain="www.acme.com"; ' 451 'path_spec; discard; version=0')) 452 old_str = repr(c) 453 c.save(ignore_expires=True, ignore_discard=True) 454 try: 455 c = MozillaCookieJar(filename) 456 c.revert(ignore_expires=True, ignore_discard=True) 457 finally: 458 os.unlink(c.filename) 459 # cookies unchanged apart from lost info re. whether path was specified 460 self.assertEqual( 461 repr(c), 462 re.sub("path_specified=%s" % True, "path_specified=%s" % False, 463 old_str) 464 ) 465 self.assertEqual(interact_netscape(c, "http://www.acme.com/foo/"), 466 '"spam"; eggs') 467 468 def test_rfc2109_handling(self): 469 # RFC 2109 cookies are handled as RFC 2965 or Netscape cookies, 470 # dependent on policy settings 471 for rfc2109_as_netscape, rfc2965, version in [ 472 # default according to rfc2965 if not explicitly specified 473 (None, False, 0), 474 (None, True, 1), 475 # explicit rfc2109_as_netscape 476 (False, False, None), # version None here means no cookie stored 477 (False, True, 1), 478 (True, False, 0), 479 (True, True, 0), 480 ]: 481 policy = DefaultCookiePolicy( 482 rfc2109_as_netscape=rfc2109_as_netscape, 483 rfc2965=rfc2965) 484 c = CookieJar(policy) 485 interact_netscape(c, "http://www.example.com/", "ni=ni; Version=1") 486 try: 487 cookie = c._cookies["www.example.com"]["/"]["ni"] 488 except KeyError: 489 self.assertIsNone(version) # didn't expect a stored cookie 490 else: 491 self.assertEqual(cookie.version, version) 492 # 2965 cookies are unaffected 493 interact_2965(c, "http://www.example.com/", 494 "foo=bar; Version=1") 495 if rfc2965: 496 cookie2965 = c._cookies["www.example.com"]["/"]["foo"] 497 self.assertEqual(cookie2965.version, 1) 498 499 def test_ns_parser(self): 500 c = CookieJar() 501 interact_netscape(c, "http://www.acme.com/", 502 'spam=eggs; DoMain=.acme.com; port; blArgh="feep"') 503 interact_netscape(c, "http://www.acme.com/", 'ni=ni; port=80,8080') 504 interact_netscape(c, "http://www.acme.com:80/", 'nini=ni') 505 interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=') 506 interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; ' 507 'expires="Foo Bar 25 33:22:11 3022"') 508 interact_netscape(c, 'http://www.acme.com/', 'fortytwo=') 509 interact_netscape(c, 'http://www.acme.com/', '=unladenswallow') 510 interact_netscape(c, 'http://www.acme.com/', 'holyhandgrenade') 511 512 cookie = c._cookies[".acme.com"]["/"]["spam"] 513 self.assertEqual(cookie.domain, ".acme.com") 514 self.assertTrue(cookie.domain_specified) 515 self.assertEqual(cookie.port, DEFAULT_HTTP_PORT) 516 self.assertFalse(cookie.port_specified) 517 # case is preserved 518 self.assertTrue(cookie.has_nonstandard_attr("blArgh")) 519 self.assertFalse(cookie.has_nonstandard_attr("blargh")) 520 521 cookie = c._cookies["www.acme.com"]["/"]["ni"] 522 self.assertEqual(cookie.domain, "www.acme.com") 523 self.assertFalse(cookie.domain_specified) 524 self.assertEqual(cookie.port, "80,8080") 525 self.assertTrue(cookie.port_specified) 526 527 cookie = c._cookies["www.acme.com"]["/"]["nini"] 528 self.assertIsNone(cookie.port) 529 self.assertFalse(cookie.port_specified) 530 531 # invalid expires should not cause cookie to be dropped 532 foo = c._cookies["www.acme.com"]["/"]["foo"] 533 spam = c._cookies["www.acme.com"]["/"]["foo"] 534 self.assertIsNone(foo.expires) 535 self.assertIsNone(spam.expires) 536 537 cookie = c._cookies['www.acme.com']['/']['fortytwo'] 538 self.assertIsNotNone(cookie.value) 539 self.assertEqual(cookie.value, '') 540 541 # there should be a distinction between a present but empty value 542 # (above) and a value that's entirely missing (below) 543 544 cookie = c._cookies['www.acme.com']['/']['holyhandgrenade'] 545 self.assertIsNone(cookie.value) 546 547 def test_ns_parser_special_names(self): 548 # names such as 'expires' are not special in first name=value pair 549 # of Set-Cookie: header 550 c = CookieJar() 551 interact_netscape(c, "http://www.acme.com/", 'expires=eggs') 552 interact_netscape(c, "http://www.acme.com/", 'version=eggs; spam=eggs') 553 554 cookies = c._cookies["www.acme.com"]["/"] 555 self.assertIn('expires', cookies) 556 self.assertIn('version', cookies) 557 558 def test_expires(self): 559 # if expires is in future, keep cookie... 560 c = CookieJar() 561 future = time2netscape(time.time()+3600) 562 interact_netscape(c, "http://www.acme.com/", 'spam="bar"; expires=%s' % 563 future) 564 self.assertEqual(len(c), 1) 565 now = time2netscape(time.time()-1) 566 # ... and if in past or present, discard it 567 interact_netscape(c, "http://www.acme.com/", 'foo="eggs"; expires=%s' % 568 now) 569 h = interact_netscape(c, "http://www.acme.com/") 570 self.assertEqual(len(c), 1) 571 self.assertIn('spam="bar"', h) 572 self.assertNotIn("foo", h) 573 574 # max-age takes precedence over expires, and zero max-age is request to 575 # delete both new cookie and any old matching cookie 576 interact_netscape(c, "http://www.acme.com/", 'eggs="bar"; expires=%s' % 577 future) 578 interact_netscape(c, "http://www.acme.com/", 'bar="bar"; expires=%s' % 579 future) 580 self.assertEqual(len(c), 3) 581 interact_netscape(c, "http://www.acme.com/", 'eggs="bar"; ' 582 'expires=%s; max-age=0' % future) 583 interact_netscape(c, "http://www.acme.com/", 'bar="bar"; ' 584 'max-age=0; expires=%s' % future) 585 h = interact_netscape(c, "http://www.acme.com/") 586 self.assertEqual(len(c), 1) 587 588 # test expiry at end of session for cookies with no expires attribute 589 interact_netscape(c, "http://www.rhubarb.net/", 'whum="fizz"') 590 self.assertEqual(len(c), 2) 591 c.clear_session_cookies() 592 self.assertEqual(len(c), 1) 593 self.assertIn('spam="bar"', h) 594 595 # test if fractional expiry is accepted 596 cookie = Cookie(0, "name", "value", 597 None, False, "www.python.org", 598 True, False, "/", 599 False, False, "1444312383.018307", 600 False, None, None, 601 {}) 602 self.assertEqual(cookie.expires, 1444312383) 603 604 # XXX RFC 2965 expiry rules (some apply to V0 too) 605 606 def test_default_path(self): 607 # RFC 2965 608 pol = DefaultCookiePolicy(rfc2965=True) 609 610 c = CookieJar(pol) 611 interact_2965(c, "http://www.acme.com/", 'spam="bar"; Version="1"') 612 self.assertIn("/", c._cookies["www.acme.com"]) 613 614 c = CookieJar(pol) 615 interact_2965(c, "http://www.acme.com/blah", 'eggs="bar"; Version="1"') 616 self.assertIn("/", c._cookies["www.acme.com"]) 617 618 c = CookieJar(pol) 619 interact_2965(c, "http://www.acme.com/blah/rhubarb", 620 'eggs="bar"; Version="1"') 621 self.assertIn("/blah/", c._cookies["www.acme.com"]) 622 623 c = CookieJar(pol) 624 interact_2965(c, "http://www.acme.com/blah/rhubarb/", 625 'eggs="bar"; Version="1"') 626 self.assertIn("/blah/rhubarb/", c._cookies["www.acme.com"]) 627 628 # Netscape 629 630 c = CookieJar() 631 interact_netscape(c, "http://www.acme.com/", 'spam="bar"') 632 self.assertIn("/", c._cookies["www.acme.com"]) 633 634 c = CookieJar() 635 interact_netscape(c, "http://www.acme.com/blah", 'eggs="bar"') 636 self.assertIn("/", c._cookies["www.acme.com"]) 637 638 c = CookieJar() 639 interact_netscape(c, "http://www.acme.com/blah/rhubarb", 'eggs="bar"') 640 self.assertIn("/blah", c._cookies["www.acme.com"]) 641 642 c = CookieJar() 643 interact_netscape(c, "http://www.acme.com/blah/rhubarb/", 'eggs="bar"') 644 self.assertIn("/blah/rhubarb", c._cookies["www.acme.com"]) 645 646 def test_default_path_with_query(self): 647 cj = CookieJar() 648 uri = "http://example.com/?spam/eggs" 649 value = 'eggs="bar"' 650 interact_netscape(cj, uri, value) 651 # Default path does not include query, so is "/", not "/?spam". 652 self.assertIn("/", cj._cookies["example.com"]) 653 # Cookie is sent back to the same URI. 654 self.assertEqual(interact_netscape(cj, uri), value) 655 656 def test_escape_path(self): 657 cases = [ 658 # quoted safe 659 ("/foo%2f/bar", "/foo%2F/bar"), 660 ("/foo%2F/bar", "/foo%2F/bar"), 661 # quoted % 662 ("/foo%%/bar", "/foo%%/bar"), 663 # quoted unsafe 664 ("/fo%19o/bar", "/fo%19o/bar"), 665 ("/fo%7do/bar", "/fo%7Do/bar"), 666 # unquoted safe 667 ("/foo/bar&", "/foo/bar&"), 668 ("/foo//bar", "/foo//bar"), 669 ("\176/foo/bar", "\176/foo/bar"), 670 # unquoted unsafe 671 ("/foo\031/bar", "/foo%19/bar"), 672 ("/\175foo/bar", "/%7Dfoo/bar"), 673 # unicode, latin-1 range 674 ("/foo/bar\u00fc", "/foo/bar%C3%BC"), # UTF-8 encoded 675 # unicode 676 ("/foo/bar\uabcd", "/foo/bar%EA%AF%8D"), # UTF-8 encoded 677 ] 678 for arg, result in cases: 679 self.assertEqual(escape_path(arg), result) 680 681 def test_request_path(self): 682 # with parameters 683 req = urllib.request.Request( 684 "http://www.example.com/rheum/rhaponticum;" 685 "foo=bar;sing=song?apples=pears&spam=eggs#ni") 686 self.assertEqual(request_path(req), 687 "/rheum/rhaponticum;foo=bar;sing=song") 688 # without parameters 689 req = urllib.request.Request( 690 "http://www.example.com/rheum/rhaponticum?" 691 "apples=pears&spam=eggs#ni") 692 self.assertEqual(request_path(req), "/rheum/rhaponticum") 693 # missing final slash 694 req = urllib.request.Request("http://www.example.com") 695 self.assertEqual(request_path(req), "/") 696 697 def test_request_port(self): 698 req = urllib.request.Request("http://www.acme.com:1234/", 699 headers={"Host": "www.acme.com:4321"}) 700 self.assertEqual(request_port(req), "1234") 701 req = urllib.request.Request("http://www.acme.com/", 702 headers={"Host": "www.acme.com:4321"}) 703 self.assertEqual(request_port(req), DEFAULT_HTTP_PORT) 704 705 def test_request_host(self): 706 # this request is illegal (RFC2616, 14.2.3) 707 req = urllib.request.Request("http://1.1.1.1/", 708 headers={"Host": "www.acme.com:80"}) 709 # libwww-perl wants this response, but that seems wrong (RFC 2616, 710 # section 5.2, point 1., and RFC 2965 section 1, paragraph 3) 711 #self.assertEqual(request_host(req), "www.acme.com") 712 self.assertEqual(request_host(req), "1.1.1.1") 713 req = urllib.request.Request("http://www.acme.com/", 714 headers={"Host": "irrelevant.com"}) 715 self.assertEqual(request_host(req), "www.acme.com") 716 # port shouldn't be in request-host 717 req = urllib.request.Request("http://www.acme.com:2345/resource.html", 718 headers={"Host": "www.acme.com:5432"}) 719 self.assertEqual(request_host(req), "www.acme.com") 720 721 def test_is_HDN(self): 722 self.assertTrue(is_HDN("foo.bar.com")) 723 self.assertTrue(is_HDN("1foo2.3bar4.5com")) 724 self.assertFalse(is_HDN("192.168.1.1")) 725 self.assertFalse(is_HDN("")) 726 self.assertFalse(is_HDN(".")) 727 self.assertFalse(is_HDN(".foo.bar.com")) 728 self.assertFalse(is_HDN("..foo")) 729 self.assertFalse(is_HDN("foo.")) 730 731 def test_reach(self): 732 self.assertEqual(reach("www.acme.com"), ".acme.com") 733 self.assertEqual(reach("acme.com"), "acme.com") 734 self.assertEqual(reach("acme.local"), ".local") 735 self.assertEqual(reach(".local"), ".local") 736 self.assertEqual(reach(".com"), ".com") 737 self.assertEqual(reach("."), ".") 738 self.assertEqual(reach(""), "") 739 self.assertEqual(reach("192.168.0.1"), "192.168.0.1") 740 741 def test_domain_match(self): 742 self.assertTrue(domain_match("192.168.1.1", "192.168.1.1")) 743 self.assertFalse(domain_match("192.168.1.1", ".168.1.1")) 744 self.assertTrue(domain_match("x.y.com", "x.Y.com")) 745 self.assertTrue(domain_match("x.y.com", ".Y.com")) 746 self.assertFalse(domain_match("x.y.com", "Y.com")) 747 self.assertTrue(domain_match("a.b.c.com", ".c.com")) 748 self.assertFalse(domain_match(".c.com", "a.b.c.com")) 749 self.assertTrue(domain_match("example.local", ".local")) 750 self.assertFalse(domain_match("blah.blah", "")) 751 self.assertFalse(domain_match("", ".rhubarb.rhubarb")) 752 self.assertTrue(domain_match("", "")) 753 754 self.assertTrue(user_domain_match("acme.com", "acme.com")) 755 self.assertFalse(user_domain_match("acme.com", ".acme.com")) 756 self.assertTrue(user_domain_match("rhubarb.acme.com", ".acme.com")) 757 self.assertTrue(user_domain_match("www.rhubarb.acme.com", ".acme.com")) 758 self.assertTrue(user_domain_match("x.y.com", "x.Y.com")) 759 self.assertTrue(user_domain_match("x.y.com", ".Y.com")) 760 self.assertFalse(user_domain_match("x.y.com", "Y.com")) 761 self.assertTrue(user_domain_match("y.com", "Y.com")) 762 self.assertFalse(user_domain_match(".y.com", "Y.com")) 763 self.assertTrue(user_domain_match(".y.com", ".Y.com")) 764 self.assertTrue(user_domain_match("x.y.com", ".com")) 765 self.assertFalse(user_domain_match("x.y.com", "com")) 766 self.assertFalse(user_domain_match("x.y.com", "m")) 767 self.assertFalse(user_domain_match("x.y.com", ".m")) 768 self.assertFalse(user_domain_match("x.y.com", "")) 769 self.assertFalse(user_domain_match("x.y.com", ".")) 770 self.assertTrue(user_domain_match("192.168.1.1", "192.168.1.1")) 771 # not both HDNs, so must string-compare equal to match 772 self.assertFalse(user_domain_match("192.168.1.1", ".168.1.1")) 773 self.assertFalse(user_domain_match("192.168.1.1", ".")) 774 # empty string is a special case 775 self.assertFalse(user_domain_match("192.168.1.1", "")) 776 777 def test_wrong_domain(self): 778 # Cookies whose effective request-host name does not domain-match the 779 # domain are rejected. 780 781 # XXX far from complete 782 c = CookieJar() 783 interact_2965(c, "http://www.nasty.com/", 784 'foo=bar; domain=friendly.org; Version="1"') 785 self.assertEqual(len(c), 0) 786 787 def test_strict_domain(self): 788 # Cookies whose domain is a country-code tld like .co.uk should 789 # not be set if CookiePolicy.strict_domain is true. 790 cp = DefaultCookiePolicy(strict_domain=True) 791 cj = CookieJar(policy=cp) 792 interact_netscape(cj, "http://example.co.uk/", 'no=problemo') 793 interact_netscape(cj, "http://example.co.uk/", 794 'okey=dokey; Domain=.example.co.uk') 795 self.assertEqual(len(cj), 2) 796 for pseudo_tld in [".co.uk", ".org.za", ".tx.us", ".name.us"]: 797 interact_netscape(cj, "http://example.%s/" % pseudo_tld, 798 'spam=eggs; Domain=.co.uk') 799 self.assertEqual(len(cj), 2) 800 801 def test_two_component_domain_ns(self): 802 # Netscape: .www.bar.com, www.bar.com, .bar.com, bar.com, no domain 803 # should all get accepted, as should .acme.com, acme.com and no domain 804 # for 2-component domains like acme.com. 805 c = CookieJar() 806 807 # two-component V0 domain is OK 808 interact_netscape(c, "http://foo.net/", 'ns=bar') 809 self.assertEqual(len(c), 1) 810 self.assertEqual(c._cookies["foo.net"]["/"]["ns"].value, "bar") 811 self.assertEqual(interact_netscape(c, "http://foo.net/"), "ns=bar") 812 # *will* be returned to any other domain (unlike RFC 2965)... 813 self.assertEqual(interact_netscape(c, "http://www.foo.net/"), 814 "ns=bar") 815 # ...unless requested otherwise 816 pol = DefaultCookiePolicy( 817 strict_ns_domain=DefaultCookiePolicy.DomainStrictNonDomain) 818 c.set_policy(pol) 819 self.assertEqual(interact_netscape(c, "http://www.foo.net/"), "") 820 821 # unlike RFC 2965, even explicit two-component domain is OK, 822 # because .foo.net matches foo.net 823 interact_netscape(c, "http://foo.net/foo/", 824 'spam1=eggs; domain=foo.net') 825 # even if starts with a dot -- in NS rules, .foo.net matches foo.net! 826 interact_netscape(c, "http://foo.net/foo/bar/", 827 'spam2=eggs; domain=.foo.net') 828 self.assertEqual(len(c), 3) 829 self.assertEqual(c._cookies[".foo.net"]["/foo"]["spam1"].value, 830 "eggs") 831 self.assertEqual(c._cookies[".foo.net"]["/foo/bar"]["spam2"].value, 832 "eggs") 833 self.assertEqual(interact_netscape(c, "http://foo.net/foo/bar/"), 834 "spam2=eggs; spam1=eggs; ns=bar") 835 836 # top-level domain is too general 837 interact_netscape(c, "http://foo.net/", 'nini="ni"; domain=.net') 838 self.assertEqual(len(c), 3) 839 840## # Netscape protocol doesn't allow non-special top level domains (such 841## # as co.uk) in the domain attribute unless there are at least three 842## # dots in it. 843 # Oh yes it does! Real implementations don't check this, and real 844 # cookies (of course) rely on that behaviour. 845 interact_netscape(c, "http://foo.co.uk", 'nasty=trick; domain=.co.uk') 846## self.assertEqual(len(c), 2) 847 self.assertEqual(len(c), 4) 848 849 def test_two_component_domain_rfc2965(self): 850 pol = DefaultCookiePolicy(rfc2965=True) 851 c = CookieJar(pol) 852 853 # two-component V1 domain is OK 854 interact_2965(c, "http://foo.net/", 'foo=bar; Version="1"') 855 self.assertEqual(len(c), 1) 856 self.assertEqual(c._cookies["foo.net"]["/"]["foo"].value, "bar") 857 self.assertEqual(interact_2965(c, "http://foo.net/"), 858 "$Version=1; foo=bar") 859 # won't be returned to any other domain (because domain was implied) 860 self.assertEqual(interact_2965(c, "http://www.foo.net/"), "") 861 862 # unless domain is given explicitly, because then it must be 863 # rewritten to start with a dot: foo.net --> .foo.net, which does 864 # not domain-match foo.net 865 interact_2965(c, "http://foo.net/foo", 866 'spam=eggs; domain=foo.net; path=/foo; Version="1"') 867 self.assertEqual(len(c), 1) 868 self.assertEqual(interact_2965(c, "http://foo.net/foo"), 869 "$Version=1; foo=bar") 870 871 # explicit foo.net from three-component domain www.foo.net *does* get 872 # set, because .foo.net domain-matches .foo.net 873 interact_2965(c, "http://www.foo.net/foo/", 874 'spam=eggs; domain=foo.net; Version="1"') 875 self.assertEqual(c._cookies[".foo.net"]["/foo/"]["spam"].value, 876 "eggs") 877 self.assertEqual(len(c), 2) 878 self.assertEqual(interact_2965(c, "http://foo.net/foo/"), 879 "$Version=1; foo=bar") 880 self.assertEqual(interact_2965(c, "http://www.foo.net/foo/"), 881 '$Version=1; spam=eggs; $Domain="foo.net"') 882 883 # top-level domain is too general 884 interact_2965(c, "http://foo.net/", 885 'ni="ni"; domain=".net"; Version="1"') 886 self.assertEqual(len(c), 2) 887 888 # RFC 2965 doesn't require blocking this 889 interact_2965(c, "http://foo.co.uk/", 890 'nasty=trick; domain=.co.uk; Version="1"') 891 self.assertEqual(len(c), 3) 892 893 def test_domain_allow(self): 894 c = CookieJar(policy=DefaultCookiePolicy( 895 blocked_domains=["acme.com"], 896 allowed_domains=["www.acme.com"])) 897 898 req = urllib.request.Request("http://acme.com/") 899 headers = ["Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/"] 900 res = FakeResponse(headers, "http://acme.com/") 901 c.extract_cookies(res, req) 902 self.assertEqual(len(c), 0) 903 904 req = urllib.request.Request("http://www.acme.com/") 905 res = FakeResponse(headers, "http://www.acme.com/") 906 c.extract_cookies(res, req) 907 self.assertEqual(len(c), 1) 908 909 req = urllib.request.Request("http://www.coyote.com/") 910 res = FakeResponse(headers, "http://www.coyote.com/") 911 c.extract_cookies(res, req) 912 self.assertEqual(len(c), 1) 913 914 # set a cookie with non-allowed domain... 915 req = urllib.request.Request("http://www.coyote.com/") 916 res = FakeResponse(headers, "http://www.coyote.com/") 917 cookies = c.make_cookies(res, req) 918 c.set_cookie(cookies[0]) 919 self.assertEqual(len(c), 2) 920 # ... and check is doesn't get returned 921 c.add_cookie_header(req) 922 self.assertFalse(req.has_header("Cookie")) 923 924 def test_domain_block(self): 925 pol = DefaultCookiePolicy( 926 rfc2965=True, blocked_domains=[".acme.com"]) 927 c = CookieJar(policy=pol) 928 headers = ["Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/"] 929 930 req = urllib.request.Request("http://www.acme.com/") 931 res = FakeResponse(headers, "http://www.acme.com/") 932 c.extract_cookies(res, req) 933 self.assertEqual(len(c), 0) 934 935 p = pol.set_blocked_domains(["acme.com"]) 936 c.extract_cookies(res, req) 937 self.assertEqual(len(c), 1) 938 939 c.clear() 940 req = urllib.request.Request("http://www.roadrunner.net/") 941 res = FakeResponse(headers, "http://www.roadrunner.net/") 942 c.extract_cookies(res, req) 943 self.assertEqual(len(c), 1) 944 req = urllib.request.Request("http://www.roadrunner.net/") 945 c.add_cookie_header(req) 946 self.assertTrue(req.has_header("Cookie")) 947 self.assertTrue(req.has_header("Cookie2")) 948 949 c.clear() 950 pol.set_blocked_domains([".acme.com"]) 951 c.extract_cookies(res, req) 952 self.assertEqual(len(c), 1) 953 954 # set a cookie with blocked domain... 955 req = urllib.request.Request("http://www.acme.com/") 956 res = FakeResponse(headers, "http://www.acme.com/") 957 cookies = c.make_cookies(res, req) 958 c.set_cookie(cookies[0]) 959 self.assertEqual(len(c), 2) 960 # ... and check is doesn't get returned 961 c.add_cookie_header(req) 962 self.assertFalse(req.has_header("Cookie")) 963 964 def test_secure(self): 965 for ns in True, False: 966 for whitespace in " ", "": 967 c = CookieJar() 968 if ns: 969 pol = DefaultCookiePolicy(rfc2965=False) 970 int = interact_netscape 971 vs = "" 972 else: 973 pol = DefaultCookiePolicy(rfc2965=True) 974 int = interact_2965 975 vs = "; Version=1" 976 c.set_policy(pol) 977 url = "http://www.acme.com/" 978 int(c, url, "foo1=bar%s%s" % (vs, whitespace)) 979 int(c, url, "foo2=bar%s; secure%s" % (vs, whitespace)) 980 self.assertFalse( 981 c._cookies["www.acme.com"]["/"]["foo1"].secure, 982 "non-secure cookie registered secure") 983 self.assertTrue( 984 c._cookies["www.acme.com"]["/"]["foo2"].secure, 985 "secure cookie registered non-secure") 986 987 def test_quote_cookie_value(self): 988 c = CookieJar(policy=DefaultCookiePolicy(rfc2965=True)) 989 interact_2965(c, "http://www.acme.com/", r'foo=\b"a"r; Version=1') 990 h = interact_2965(c, "http://www.acme.com/") 991 self.assertEqual(h, r'$Version=1; foo=\\b\"a\"r') 992 993 def test_missing_final_slash(self): 994 # Missing slash from request URL's abs_path should be assumed present. 995 url = "http://www.acme.com" 996 c = CookieJar(DefaultCookiePolicy(rfc2965=True)) 997 interact_2965(c, url, "foo=bar; Version=1") 998 req = urllib.request.Request(url) 999 self.assertEqual(len(c), 1) 1000 c.add_cookie_header(req) 1001 self.assertTrue(req.has_header("Cookie")) 1002 1003 def test_domain_mirror(self): 1004 pol = DefaultCookiePolicy(rfc2965=True) 1005 1006 c = CookieJar(pol) 1007 url = "http://foo.bar.com/" 1008 interact_2965(c, url, "spam=eggs; Version=1") 1009 h = interact_2965(c, url) 1010 self.assertNotIn("Domain", h, 1011 "absent domain returned with domain present") 1012 1013 c = CookieJar(pol) 1014 url = "http://foo.bar.com/" 1015 interact_2965(c, url, 'spam=eggs; Version=1; Domain=.bar.com') 1016 h = interact_2965(c, url) 1017 self.assertIn('$Domain=".bar.com"', h, "domain not returned") 1018 1019 c = CookieJar(pol) 1020 url = "http://foo.bar.com/" 1021 # note missing initial dot in Domain 1022 interact_2965(c, url, 'spam=eggs; Version=1; Domain=bar.com') 1023 h = interact_2965(c, url) 1024 self.assertIn('$Domain="bar.com"', h, "domain not returned") 1025 1026 def test_path_mirror(self): 1027 pol = DefaultCookiePolicy(rfc2965=True) 1028 1029 c = CookieJar(pol) 1030 url = "http://foo.bar.com/" 1031 interact_2965(c, url, "spam=eggs; Version=1") 1032 h = interact_2965(c, url) 1033 self.assertNotIn("Path", h, "absent path returned with path present") 1034 1035 c = CookieJar(pol) 1036 url = "http://foo.bar.com/" 1037 interact_2965(c, url, 'spam=eggs; Version=1; Path=/') 1038 h = interact_2965(c, url) 1039 self.assertIn('$Path="/"', h, "path not returned") 1040 1041 def test_port_mirror(self): 1042 pol = DefaultCookiePolicy(rfc2965=True) 1043 1044 c = CookieJar(pol) 1045 url = "http://foo.bar.com/" 1046 interact_2965(c, url, "spam=eggs; Version=1") 1047 h = interact_2965(c, url) 1048 self.assertNotIn("Port", h, "absent port returned with port present") 1049 1050 c = CookieJar(pol) 1051 url = "http://foo.bar.com/" 1052 interact_2965(c, url, "spam=eggs; Version=1; Port") 1053 h = interact_2965(c, url) 1054 self.assertRegex(h, r"\$Port([^=]|$)", 1055 "port with no value not returned with no value") 1056 1057 c = CookieJar(pol) 1058 url = "http://foo.bar.com/" 1059 interact_2965(c, url, 'spam=eggs; Version=1; Port="80"') 1060 h = interact_2965(c, url) 1061 self.assertIn('$Port="80"', h, 1062 "port with single value not returned with single value") 1063 1064 c = CookieJar(pol) 1065 url = "http://foo.bar.com/" 1066 interact_2965(c, url, 'spam=eggs; Version=1; Port="80,8080"') 1067 h = interact_2965(c, url) 1068 self.assertIn('$Port="80,8080"', h, 1069 "port with multiple values not returned with multiple " 1070 "values") 1071 1072 def test_no_return_comment(self): 1073 c = CookieJar(DefaultCookiePolicy(rfc2965=True)) 1074 url = "http://foo.bar.com/" 1075 interact_2965(c, url, 'spam=eggs; Version=1; ' 1076 'Comment="does anybody read these?"; ' 1077 'CommentURL="http://foo.bar.net/comment.html"') 1078 h = interact_2965(c, url) 1079 self.assertNotIn("Comment", h, 1080 "Comment or CommentURL cookie-attributes returned to server") 1081 1082 def test_Cookie_iterator(self): 1083 cs = CookieJar(DefaultCookiePolicy(rfc2965=True)) 1084 # add some random cookies 1085 interact_2965(cs, "http://blah.spam.org/", 'foo=eggs; Version=1; ' 1086 'Comment="does anybody read these?"; ' 1087 'CommentURL="http://foo.bar.net/comment.html"') 1088 interact_netscape(cs, "http://www.acme.com/blah/", "spam=bar; secure") 1089 interact_2965(cs, "http://www.acme.com/blah/", 1090 "foo=bar; secure; Version=1") 1091 interact_2965(cs, "http://www.acme.com/blah/", 1092 "foo=bar; path=/; Version=1") 1093 interact_2965(cs, "http://www.sol.no", 1094 r'bang=wallop; version=1; domain=".sol.no"; ' 1095 r'port="90,100, 80,8080"; ' 1096 r'max-age=100; Comment = "Just kidding! (\"|\\\\) "') 1097 1098 versions = [1, 1, 1, 0, 1] 1099 names = ["bang", "foo", "foo", "spam", "foo"] 1100 domains = [".sol.no", "blah.spam.org", "www.acme.com", 1101 "www.acme.com", "www.acme.com"] 1102 paths = ["/", "/", "/", "/blah", "/blah/"] 1103 1104 for i in range(4): 1105 i = 0 1106 for c in cs: 1107 self.assertIsInstance(c, Cookie) 1108 self.assertEqual(c.version, versions[i]) 1109 self.assertEqual(c.name, names[i]) 1110 self.assertEqual(c.domain, domains[i]) 1111 self.assertEqual(c.path, paths[i]) 1112 i = i + 1 1113 1114 def test_parse_ns_headers(self): 1115 # missing domain value (invalid cookie) 1116 self.assertEqual( 1117 parse_ns_headers(["foo=bar; path=/; domain"]), 1118 [[("foo", "bar"), 1119 ("path", "/"), ("domain", None), ("version", "0")]] 1120 ) 1121 # invalid expires value 1122 self.assertEqual( 1123 parse_ns_headers(["foo=bar; expires=Foo Bar 12 33:22:11 2000"]), 1124 [[("foo", "bar"), ("expires", None), ("version", "0")]] 1125 ) 1126 # missing cookie value (valid cookie) 1127 self.assertEqual( 1128 parse_ns_headers(["foo"]), 1129 [[("foo", None), ("version", "0")]] 1130 ) 1131 # missing cookie values for parsed attributes 1132 self.assertEqual( 1133 parse_ns_headers(['foo=bar; expires']), 1134 [[('foo', 'bar'), ('expires', None), ('version', '0')]]) 1135 self.assertEqual( 1136 parse_ns_headers(['foo=bar; version']), 1137 [[('foo', 'bar'), ('version', None)]]) 1138 # shouldn't add version if header is empty 1139 self.assertEqual(parse_ns_headers([""]), []) 1140 1141 def test_bad_cookie_header(self): 1142 1143 def cookiejar_from_cookie_headers(headers): 1144 c = CookieJar() 1145 req = urllib.request.Request("http://www.example.com/") 1146 r = FakeResponse(headers, "http://www.example.com/") 1147 c.extract_cookies(r, req) 1148 return c 1149 1150 future = time2netscape(time.time()+3600) 1151 1152 # none of these bad headers should cause an exception to be raised 1153 for headers in [ 1154 ["Set-Cookie: "], # actually, nothing wrong with this 1155 ["Set-Cookie2: "], # ditto 1156 # missing domain value 1157 ["Set-Cookie2: a=foo; path=/; Version=1; domain"], 1158 # bad max-age 1159 ["Set-Cookie: b=foo; max-age=oops"], 1160 # bad version 1161 ["Set-Cookie: b=foo; version=spam"], 1162 ["Set-Cookie:; Expires=%s" % future], 1163 ]: 1164 c = cookiejar_from_cookie_headers(headers) 1165 # these bad cookies shouldn't be set 1166 self.assertEqual(len(c), 0) 1167 1168 # cookie with invalid expires is treated as session cookie 1169 headers = ["Set-Cookie: c=foo; expires=Foo Bar 12 33:22:11 2000"] 1170 c = cookiejar_from_cookie_headers(headers) 1171 cookie = c._cookies["www.example.com"]["/"]["c"] 1172 self.assertIsNone(cookie.expires) 1173 1174 1175class LWPCookieTests(unittest.TestCase): 1176 # Tests taken from libwww-perl, with a few modifications and additions. 1177 1178 def test_netscape_example_1(self): 1179 #------------------------------------------------------------------- 1180 # First we check that it works for the original example at 1181 # http://www.netscape.com/newsref/std/cookie_spec.html 1182 1183 # Client requests a document, and receives in the response: 1184 # 1185 # Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT 1186 # 1187 # When client requests a URL in path "/" on this server, it sends: 1188 # 1189 # Cookie: CUSTOMER=WILE_E_COYOTE 1190 # 1191 # Client requests a document, and receives in the response: 1192 # 1193 # Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/ 1194 # 1195 # When client requests a URL in path "/" on this server, it sends: 1196 # 1197 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001 1198 # 1199 # Client receives: 1200 # 1201 # Set-Cookie: SHIPPING=FEDEX; path=/fo 1202 # 1203 # When client requests a URL in path "/" on this server, it sends: 1204 # 1205 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001 1206 # 1207 # When client requests a URL in path "/foo" on this server, it sends: 1208 # 1209 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX 1210 # 1211 # The last Cookie is buggy, because both specifications say that the 1212 # most specific cookie must be sent first. SHIPPING=FEDEX is the 1213 # most specific and should thus be first. 1214 1215 year_plus_one = time.localtime()[0] + 1 1216 1217 headers = [] 1218 1219 c = CookieJar(DefaultCookiePolicy(rfc2965 = True)) 1220 1221 #req = urllib.request.Request("http://1.1.1.1/", 1222 # headers={"Host": "www.acme.com:80"}) 1223 req = urllib.request.Request("http://www.acme.com:80/", 1224 headers={"Host": "www.acme.com:80"}) 1225 1226 headers.append( 1227 "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/ ; " 1228 "expires=Wednesday, 09-Nov-%d 23:12:40 GMT" % year_plus_one) 1229 res = FakeResponse(headers, "http://www.acme.com/") 1230 c.extract_cookies(res, req) 1231 1232 req = urllib.request.Request("http://www.acme.com/") 1233 c.add_cookie_header(req) 1234 1235 self.assertEqual(req.get_header("Cookie"), "CUSTOMER=WILE_E_COYOTE") 1236 self.assertEqual(req.get_header("Cookie2"), '$Version="1"') 1237 1238 headers.append("Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/") 1239 res = FakeResponse(headers, "http://www.acme.com/") 1240 c.extract_cookies(res, req) 1241 1242 req = urllib.request.Request("http://www.acme.com/foo/bar") 1243 c.add_cookie_header(req) 1244 1245 h = req.get_header("Cookie") 1246 self.assertIn("PART_NUMBER=ROCKET_LAUNCHER_0001", h) 1247 self.assertIn("CUSTOMER=WILE_E_COYOTE", h) 1248 1249 headers.append('Set-Cookie: SHIPPING=FEDEX; path=/foo') 1250 res = FakeResponse(headers, "http://www.acme.com") 1251 c.extract_cookies(res, req) 1252 1253 req = urllib.request.Request("http://www.acme.com/") 1254 c.add_cookie_header(req) 1255 1256 h = req.get_header("Cookie") 1257 self.assertIn("PART_NUMBER=ROCKET_LAUNCHER_0001", h) 1258 self.assertIn("CUSTOMER=WILE_E_COYOTE", h) 1259 self.assertNotIn("SHIPPING=FEDEX", h) 1260 1261 req = urllib.request.Request("http://www.acme.com/foo/") 1262 c.add_cookie_header(req) 1263 1264 h = req.get_header("Cookie") 1265 self.assertIn("PART_NUMBER=ROCKET_LAUNCHER_0001", h) 1266 self.assertIn("CUSTOMER=WILE_E_COYOTE", h) 1267 self.assertTrue(h.startswith("SHIPPING=FEDEX;")) 1268 1269 def test_netscape_example_2(self): 1270 # Second Example transaction sequence: 1271 # 1272 # Assume all mappings from above have been cleared. 1273 # 1274 # Client receives: 1275 # 1276 # Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/ 1277 # 1278 # When client requests a URL in path "/" on this server, it sends: 1279 # 1280 # Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001 1281 # 1282 # Client receives: 1283 # 1284 # Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo 1285 # 1286 # When client requests a URL in path "/ammo" on this server, it sends: 1287 # 1288 # Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001 1289 # 1290 # NOTE: There are two name/value pairs named "PART_NUMBER" due to 1291 # the inheritance of the "/" mapping in addition to the "/ammo" mapping. 1292 1293 c = CookieJar() 1294 headers = [] 1295 1296 req = urllib.request.Request("http://www.acme.com/") 1297 headers.append("Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/") 1298 res = FakeResponse(headers, "http://www.acme.com/") 1299 1300 c.extract_cookies(res, req) 1301 1302 req = urllib.request.Request("http://www.acme.com/") 1303 c.add_cookie_header(req) 1304 1305 self.assertEqual(req.get_header("Cookie"), 1306 "PART_NUMBER=ROCKET_LAUNCHER_0001") 1307 1308 headers.append( 1309 "Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo") 1310 res = FakeResponse(headers, "http://www.acme.com/") 1311 c.extract_cookies(res, req) 1312 1313 req = urllib.request.Request("http://www.acme.com/ammo") 1314 c.add_cookie_header(req) 1315 1316 self.assertRegex(req.get_header("Cookie"), 1317 r"PART_NUMBER=RIDING_ROCKET_0023;\s*" 1318 "PART_NUMBER=ROCKET_LAUNCHER_0001") 1319 1320 def test_ietf_example_1(self): 1321 #------------------------------------------------------------------- 1322 # Then we test with the examples from draft-ietf-http-state-man-mec-03.txt 1323 # 1324 # 5. EXAMPLES 1325 1326 c = CookieJar(DefaultCookiePolicy(rfc2965=True)) 1327 1328 # 1329 # 5.1 Example 1 1330 # 1331 # Most detail of request and response headers has been omitted. Assume 1332 # the user agent has no stored cookies. 1333 # 1334 # 1. User Agent -> Server 1335 # 1336 # POST /acme/login HTTP/1.1 1337 # [form data] 1338 # 1339 # User identifies self via a form. 1340 # 1341 # 2. Server -> User Agent 1342 # 1343 # HTTP/1.1 200 OK 1344 # Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme" 1345 # 1346 # Cookie reflects user's identity. 1347 1348 cookie = interact_2965( 1349 c, 'http://www.acme.com/acme/login', 1350 'Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"') 1351 self.assertFalse(cookie) 1352 1353 # 1354 # 3. User Agent -> Server 1355 # 1356 # POST /acme/pickitem HTTP/1.1 1357 # Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme" 1358 # [form data] 1359 # 1360 # User selects an item for ``shopping basket.'' 1361 # 1362 # 4. Server -> User Agent 1363 # 1364 # HTTP/1.1 200 OK 1365 # Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1"; 1366 # Path="/acme" 1367 # 1368 # Shopping basket contains an item. 1369 1370 cookie = interact_2965(c, 'http://www.acme.com/acme/pickitem', 1371 'Part_Number="Rocket_Launcher_0001"; ' 1372 'Version="1"; Path="/acme"'); 1373 self.assertRegex(cookie, 1374 r'^\$Version="?1"?; Customer="?WILE_E_COYOTE"?; \$Path="/acme"$') 1375 1376 # 1377 # 5. User Agent -> Server 1378 # 1379 # POST /acme/shipping HTTP/1.1 1380 # Cookie: $Version="1"; 1381 # Customer="WILE_E_COYOTE"; $Path="/acme"; 1382 # Part_Number="Rocket_Launcher_0001"; $Path="/acme" 1383 # [form data] 1384 # 1385 # User selects shipping method from form. 1386 # 1387 # 6. Server -> User Agent 1388 # 1389 # HTTP/1.1 200 OK 1390 # Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme" 1391 # 1392 # New cookie reflects shipping method. 1393 1394 cookie = interact_2965(c, "http://www.acme.com/acme/shipping", 1395 'Shipping="FedEx"; Version="1"; Path="/acme"') 1396 1397 self.assertRegex(cookie, r'^\$Version="?1"?;') 1398 self.assertRegex(cookie, r'Part_Number="?Rocket_Launcher_0001"?;' 1399 r'\s*\$Path="\/acme"') 1400 self.assertRegex(cookie, r'Customer="?WILE_E_COYOTE"?;' 1401 r'\s*\$Path="\/acme"') 1402 1403 # 1404 # 7. User Agent -> Server 1405 # 1406 # POST /acme/process HTTP/1.1 1407 # Cookie: $Version="1"; 1408 # Customer="WILE_E_COYOTE"; $Path="/acme"; 1409 # Part_Number="Rocket_Launcher_0001"; $Path="/acme"; 1410 # Shipping="FedEx"; $Path="/acme" 1411 # [form data] 1412 # 1413 # User chooses to process order. 1414 # 1415 # 8. Server -> User Agent 1416 # 1417 # HTTP/1.1 200 OK 1418 # 1419 # Transaction is complete. 1420 1421 cookie = interact_2965(c, "http://www.acme.com/acme/process") 1422 self.assertRegex(cookie, r'Shipping="?FedEx"?;\s*\$Path="\/acme"') 1423 self.assertIn("WILE_E_COYOTE", cookie) 1424 1425 # 1426 # The user agent makes a series of requests on the origin server, after 1427 # each of which it receives a new cookie. All the cookies have the same 1428 # Path attribute and (default) domain. Because the request URLs all have 1429 # /acme as a prefix, and that matches the Path attribute, each request 1430 # contains all the cookies received so far. 1431 1432 def test_ietf_example_2(self): 1433 # 5.2 Example 2 1434 # 1435 # This example illustrates the effect of the Path attribute. All detail 1436 # of request and response headers has been omitted. Assume the user agent 1437 # has no stored cookies. 1438 1439 c = CookieJar(DefaultCookiePolicy(rfc2965=True)) 1440 1441 # Imagine the user agent has received, in response to earlier requests, 1442 # the response headers 1443 # 1444 # Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1"; 1445 # Path="/acme" 1446 # 1447 # and 1448 # 1449 # Set-Cookie2: Part_Number="Riding_Rocket_0023"; Version="1"; 1450 # Path="/acme/ammo" 1451 1452 interact_2965( 1453 c, "http://www.acme.com/acme/ammo/specific", 1454 'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"', 1455 'Part_Number="Riding_Rocket_0023"; Version="1"; Path="/acme/ammo"') 1456 1457 # A subsequent request by the user agent to the (same) server for URLs of 1458 # the form /acme/ammo/... would include the following request header: 1459 # 1460 # Cookie: $Version="1"; 1461 # Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo"; 1462 # Part_Number="Rocket_Launcher_0001"; $Path="/acme" 1463 # 1464 # Note that the NAME=VALUE pair for the cookie with the more specific Path 1465 # attribute, /acme/ammo, comes before the one with the less specific Path 1466 # attribute, /acme. Further note that the same cookie name appears more 1467 # than once. 1468 1469 cookie = interact_2965(c, "http://www.acme.com/acme/ammo/...") 1470 self.assertRegex(cookie, r"Riding_Rocket_0023.*Rocket_Launcher_0001") 1471 1472 # A subsequent request by the user agent to the (same) server for a URL of 1473 # the form /acme/parts/ would include the following request header: 1474 # 1475 # Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001"; $Path="/acme" 1476 # 1477 # Here, the second cookie's Path attribute /acme/ammo is not a prefix of 1478 # the request URL, /acme/parts/, so the cookie does not get forwarded to 1479 # the server. 1480 1481 cookie = interact_2965(c, "http://www.acme.com/acme/parts/") 1482 self.assertIn("Rocket_Launcher_0001", cookie) 1483 self.assertNotIn("Riding_Rocket_0023", cookie) 1484 1485 def test_rejection(self): 1486 # Test rejection of Set-Cookie2 responses based on domain, path, port. 1487 pol = DefaultCookiePolicy(rfc2965=True) 1488 1489 c = LWPCookieJar(policy=pol) 1490 1491 max_age = "max-age=3600" 1492 1493 # illegal domain (no embedded dots) 1494 cookie = interact_2965(c, "http://www.acme.com", 1495 'foo=bar; domain=".com"; version=1') 1496 self.assertFalse(c) 1497 1498 # legal domain 1499 cookie = interact_2965(c, "http://www.acme.com", 1500 'ping=pong; domain="acme.com"; version=1') 1501 self.assertEqual(len(c), 1) 1502 1503 # illegal domain (host prefix "www.a" contains a dot) 1504 cookie = interact_2965(c, "http://www.a.acme.com", 1505 'whiz=bang; domain="acme.com"; version=1') 1506 self.assertEqual(len(c), 1) 1507 1508 # legal domain 1509 cookie = interact_2965(c, "http://www.a.acme.com", 1510 'wow=flutter; domain=".a.acme.com"; version=1') 1511 self.assertEqual(len(c), 2) 1512 1513 # can't partially match an IP-address 1514 cookie = interact_2965(c, "http://125.125.125.125", 1515 'zzzz=ping; domain="125.125.125"; version=1') 1516 self.assertEqual(len(c), 2) 1517 1518 # illegal path (must be prefix of request path) 1519 cookie = interact_2965(c, "http://www.sol.no", 1520 'blah=rhubarb; domain=".sol.no"; path="/foo"; ' 1521 'version=1') 1522 self.assertEqual(len(c), 2) 1523 1524 # legal path 1525 cookie = interact_2965(c, "http://www.sol.no/foo/bar", 1526 'bing=bong; domain=".sol.no"; path="/foo"; ' 1527 'version=1') 1528 self.assertEqual(len(c), 3) 1529 1530 # illegal port (request-port not in list) 1531 cookie = interact_2965(c, "http://www.sol.no", 1532 'whiz=ffft; domain=".sol.no"; port="90,100"; ' 1533 'version=1') 1534 self.assertEqual(len(c), 3) 1535 1536 # legal port 1537 cookie = interact_2965( 1538 c, "http://www.sol.no", 1539 r'bang=wallop; version=1; domain=".sol.no"; ' 1540 r'port="90,100, 80,8080"; ' 1541 r'max-age=100; Comment = "Just kidding! (\"|\\\\) "') 1542 self.assertEqual(len(c), 4) 1543 1544 # port attribute without any value (current port) 1545 cookie = interact_2965(c, "http://www.sol.no", 1546 'foo9=bar; version=1; domain=".sol.no"; port; ' 1547 'max-age=100;') 1548 self.assertEqual(len(c), 5) 1549 1550 # encoded path 1551 # LWP has this test, but unescaping allowed path characters seems 1552 # like a bad idea, so I think this should fail: 1553## cookie = interact_2965(c, "http://www.sol.no/foo/", 1554## r'foo8=bar; version=1; path="/%66oo"') 1555 # but this is OK, because '<' is not an allowed HTTP URL path 1556 # character: 1557 cookie = interact_2965(c, "http://www.sol.no/<oo/", 1558 r'foo8=bar; version=1; path="/%3coo"') 1559 self.assertEqual(len(c), 6) 1560 1561 # save and restore 1562 filename = test.support.TESTFN 1563 1564 try: 1565 c.save(filename, ignore_discard=True) 1566 old = repr(c) 1567 1568 c = LWPCookieJar(policy=pol) 1569 c.load(filename, ignore_discard=True) 1570 finally: 1571 try: os.unlink(filename) 1572 except OSError: pass 1573 1574 self.assertEqual(old, repr(c)) 1575 1576 def test_url_encoding(self): 1577 # Try some URL encodings of the PATHs. 1578 # (the behaviour here has changed from libwww-perl) 1579 c = CookieJar(DefaultCookiePolicy(rfc2965=True)) 1580 interact_2965(c, "http://www.acme.com/foo%2f%25/" 1581 "%3c%3c%0Anew%C3%A5/%C3%A5", 1582 "foo = bar; version = 1") 1583 1584 cookie = interact_2965( 1585 c, "http://www.acme.com/foo%2f%25/<<%0anew\345/\346\370\345", 1586 'bar=baz; path="/foo/"; version=1'); 1587 version_re = re.compile(r'^\$version=\"?1\"?', re.I) 1588 self.assertIn("foo=bar", cookie) 1589 self.assertRegex(cookie, version_re) 1590 1591 cookie = interact_2965( 1592 c, "http://www.acme.com/foo/%25/<<%0anew\345/\346\370\345") 1593 self.assertFalse(cookie) 1594 1595 # unicode URL doesn't raise exception 1596 cookie = interact_2965(c, "http://www.acme.com/\xfc") 1597 1598 def test_mozilla(self): 1599 # Save / load Mozilla/Netscape cookie file format. 1600 year_plus_one = time.localtime()[0] + 1 1601 1602 filename = test.support.TESTFN 1603 1604 c = MozillaCookieJar(filename, 1605 policy=DefaultCookiePolicy(rfc2965=True)) 1606 interact_2965(c, "http://www.acme.com/", 1607 "foo1=bar; max-age=100; Version=1") 1608 interact_2965(c, "http://www.acme.com/", 1609 'foo2=bar; port="80"; max-age=100; Discard; Version=1') 1610 interact_2965(c, "http://www.acme.com/", "foo3=bar; secure; Version=1") 1611 1612 expires = "expires=09-Nov-%d 23:12:40 GMT" % (year_plus_one,) 1613 interact_netscape(c, "http://www.foo.com/", 1614 "fooa=bar; %s" % expires) 1615 interact_netscape(c, "http://www.foo.com/", 1616 "foob=bar; Domain=.foo.com; %s" % expires) 1617 interact_netscape(c, "http://www.foo.com/", 1618 "fooc=bar; Domain=www.foo.com; %s" % expires) 1619 1620 def save_and_restore(cj, ignore_discard): 1621 try: 1622 cj.save(ignore_discard=ignore_discard) 1623 new_c = MozillaCookieJar(filename, 1624 DefaultCookiePolicy(rfc2965=True)) 1625 new_c.load(ignore_discard=ignore_discard) 1626 finally: 1627 try: os.unlink(filename) 1628 except OSError: pass 1629 return new_c 1630 1631 new_c = save_and_restore(c, True) 1632 self.assertEqual(len(new_c), 6) # none discarded 1633 self.assertIn("name='foo1', value='bar'", repr(new_c)) 1634 1635 new_c = save_and_restore(c, False) 1636 self.assertEqual(len(new_c), 4) # 2 of them discarded on save 1637 self.assertIn("name='foo1', value='bar'", repr(new_c)) 1638 1639 def test_netscape_misc(self): 1640 # Some additional Netscape cookies tests. 1641 c = CookieJar() 1642 headers = [] 1643 req = urllib.request.Request("http://foo.bar.acme.com/foo") 1644 1645 # Netscape allows a host part that contains dots 1646 headers.append("Set-Cookie: Customer=WILE_E_COYOTE; domain=.acme.com") 1647 res = FakeResponse(headers, "http://www.acme.com/foo") 1648 c.extract_cookies(res, req) 1649 1650 # and that the domain is the same as the host without adding a leading 1651 # dot to the domain. Should not quote even if strange chars are used 1652 # in the cookie value. 1653 headers.append("Set-Cookie: PART_NUMBER=3,4; domain=foo.bar.acme.com") 1654 res = FakeResponse(headers, "http://www.acme.com/foo") 1655 c.extract_cookies(res, req) 1656 1657 req = urllib.request.Request("http://foo.bar.acme.com/foo") 1658 c.add_cookie_header(req) 1659 self.assertIn("PART_NUMBER=3,4", req.get_header("Cookie")) 1660 self.assertIn("Customer=WILE_E_COYOTE",req.get_header("Cookie")) 1661 1662 def test_intranet_domains_2965(self): 1663 # Test handling of local intranet hostnames without a dot. 1664 c = CookieJar(DefaultCookiePolicy(rfc2965=True)) 1665 interact_2965(c, "http://example/", 1666 "foo1=bar; PORT; Discard; Version=1;") 1667 cookie = interact_2965(c, "http://example/", 1668 'foo2=bar; domain=".local"; Version=1') 1669 self.assertIn("foo1=bar", cookie) 1670 1671 interact_2965(c, "http://example/", 'foo3=bar; Version=1') 1672 cookie = interact_2965(c, "http://example/") 1673 self.assertIn("foo2=bar", cookie) 1674 self.assertEqual(len(c), 3) 1675 1676 def test_intranet_domains_ns(self): 1677 c = CookieJar(DefaultCookiePolicy(rfc2965 = False)) 1678 interact_netscape(c, "http://example/", "foo1=bar") 1679 cookie = interact_netscape(c, "http://example/", 1680 'foo2=bar; domain=.local') 1681 self.assertEqual(len(c), 2) 1682 self.assertIn("foo1=bar", cookie) 1683 1684 cookie = interact_netscape(c, "http://example/") 1685 self.assertIn("foo2=bar", cookie) 1686 self.assertEqual(len(c), 2) 1687 1688 def test_empty_path(self): 1689 # Test for empty path 1690 # Broken web-server ORION/1.3.38 returns to the client response like 1691 # 1692 # Set-Cookie: JSESSIONID=ABCDERANDOM123; Path= 1693 # 1694 # ie. with Path set to nothing. 1695 # In this case, extract_cookies() must set cookie to / (root) 1696 c = CookieJar(DefaultCookiePolicy(rfc2965 = True)) 1697 headers = [] 1698 1699 req = urllib.request.Request("http://www.ants.com/") 1700 headers.append("Set-Cookie: JSESSIONID=ABCDERANDOM123; Path=") 1701 res = FakeResponse(headers, "http://www.ants.com/") 1702 c.extract_cookies(res, req) 1703 1704 req = urllib.request.Request("http://www.ants.com/") 1705 c.add_cookie_header(req) 1706 1707 self.assertEqual(req.get_header("Cookie"), 1708 "JSESSIONID=ABCDERANDOM123") 1709 self.assertEqual(req.get_header("Cookie2"), '$Version="1"') 1710 1711 # missing path in the request URI 1712 req = urllib.request.Request("http://www.ants.com:8080") 1713 c.add_cookie_header(req) 1714 1715 self.assertEqual(req.get_header("Cookie"), 1716 "JSESSIONID=ABCDERANDOM123") 1717 self.assertEqual(req.get_header("Cookie2"), '$Version="1"') 1718 1719 def test_session_cookies(self): 1720 year_plus_one = time.localtime()[0] + 1 1721 1722 # Check session cookies are deleted properly by 1723 # CookieJar.clear_session_cookies method 1724 1725 req = urllib.request.Request('http://www.perlmeister.com/scripts') 1726 headers = [] 1727 headers.append("Set-Cookie: s1=session;Path=/scripts") 1728 headers.append("Set-Cookie: p1=perm; Domain=.perlmeister.com;" 1729 "Path=/;expires=Fri, 02-Feb-%d 23:24:20 GMT" % 1730 year_plus_one) 1731 headers.append("Set-Cookie: p2=perm;Path=/;expires=Fri, " 1732 "02-Feb-%d 23:24:20 GMT" % year_plus_one) 1733 headers.append("Set-Cookie: s2=session;Path=/scripts;" 1734 "Domain=.perlmeister.com") 1735 headers.append('Set-Cookie2: s3=session;Version=1;Discard;Path="/"') 1736 res = FakeResponse(headers, 'http://www.perlmeister.com/scripts') 1737 1738 c = CookieJar() 1739 c.extract_cookies(res, req) 1740 # How many session/permanent cookies do we have? 1741 counter = {"session_after": 0, 1742 "perm_after": 0, 1743 "session_before": 0, 1744 "perm_before": 0} 1745 for cookie in c: 1746 key = "%s_before" % cookie.value 1747 counter[key] = counter[key] + 1 1748 c.clear_session_cookies() 1749 # How many now? 1750 for cookie in c: 1751 key = "%s_after" % cookie.value 1752 counter[key] = counter[key] + 1 1753 1754 # a permanent cookie got lost accidentally 1755 self.assertEqual(counter["perm_after"], counter["perm_before"]) 1756 # a session cookie hasn't been cleared 1757 self.assertEqual(counter["session_after"], 0) 1758 # we didn't have session cookies in the first place 1759 self.assertNotEqual(counter["session_before"], 0) 1760 1761 1762def test_main(verbose=None): 1763 test.support.run_unittest( 1764 DateTimeTests, 1765 HeaderTests, 1766 CookieTests, 1767 FileCookieJarTests, 1768 LWPCookieTests, 1769 ) 1770 1771if __name__ == "__main__": 1772 test_main(verbose=True) 1773