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