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