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