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