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