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