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