blob: 8394f8ea6e86bd3aa66c9a22a08be361e767fd99 [file] [log] [blame]
Brett Cannon0bfac6e2008-07-02 21:52:42 +00001# -*- coding: latin-1 -*-
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00002"""Tests for cookielib.py."""
3
4import re, os, time
5from unittest import TestCase
6
7from test import test_support
8
9class DateTimeTests(TestCase):
10
11 def test_time2isoz(self):
12 from cookielib import time2isoz
13
14 base = 1019227000
15 day = 24*3600
16 self.assertEquals(time2isoz(base), "2002-04-19 14:36:40Z")
17 self.assertEquals(time2isoz(base+day), "2002-04-20 14:36:40Z")
18 self.assertEquals(time2isoz(base+2*day), "2002-04-21 14:36:40Z")
19 self.assertEquals(time2isoz(base+3*day), "2002-04-22 14:36:40Z")
20
21 az = time2isoz()
22 bz = time2isoz(500000)
23 for text in (az, bz):
24 self.assert_(re.search(r"^\d{4}-\d\d-\d\d \d\d:\d\d:\d\dZ$", text),
25 "bad time2isoz format: %s %s" % (az, bz))
26
27 def test_http2time(self):
28 from cookielib import http2time
29
30 def parse_date(text):
31 return time.gmtime(http2time(text))[:6]
32
33 self.assertEquals(parse_date("01 Jan 2001"), (2001, 1, 1, 0, 0, 0.0))
34
35 # this test will break around year 2070
36 self.assertEquals(parse_date("03-Feb-20"), (2020, 2, 3, 0, 0, 0.0))
37
38 # this test will break around year 2048
39 self.assertEquals(parse_date("03-Feb-98"), (1998, 2, 3, 0, 0, 0.0))
40
41 def test_http2time_formats(self):
42 from cookielib import http2time, time2isoz
43
44 # test http2time for supported dates. Test cases with 2 digit year
45 # will probably break in year 2044.
46 tests = [
47 'Thu, 03 Feb 1994 00:00:00 GMT', # proposed new HTTP format
48 'Thursday, 03-Feb-94 00:00:00 GMT', # old rfc850 HTTP format
49 'Thursday, 03-Feb-1994 00:00:00 GMT', # broken rfc850 HTTP format
50
51 '03 Feb 1994 00:00:00 GMT', # HTTP format (no weekday)
52 '03-Feb-94 00:00:00 GMT', # old rfc850 (no weekday)
53 '03-Feb-1994 00:00:00 GMT', # broken rfc850 (no weekday)
54 '03-Feb-1994 00:00 GMT', # broken rfc850 (no weekday, no seconds)
55 '03-Feb-1994 00:00', # broken rfc850 (no weekday, no seconds, no tz)
56
57 '03-Feb-94', # old rfc850 HTTP format (no weekday, no time)
58 '03-Feb-1994', # broken rfc850 HTTP format (no weekday, no time)
59 '03 Feb 1994', # proposed new HTTP format (no weekday, no time)
60
61 # A few tests with extra space at various places
62 ' 03 Feb 1994 0:00 ',
63 ' 03-Feb-1994 ',
64 ]
65
66 test_t = 760233600 # assume broken POSIX counting of seconds
67 result = time2isoz(test_t)
68 expected = "1994-02-03 00:00:00Z"
69 self.assertEquals(result, expected,
70 "%s => '%s' (%s)" % (test_t, result, expected))
71
72 for s in tests:
73 t = http2time(s)
74 t2 = http2time(s.lower())
75 t3 = http2time(s.upper())
76
77 self.assert_(t == t2 == t3 == test_t,
78 "'%s' => %s, %s, %s (%s)" % (s, t, t2, t3, test_t))
79
80 def test_http2time_garbage(self):
81 from cookielib import http2time
82
83 for test in [
84 '',
85 'Garbage',
86 'Mandag 16. September 1996',
87 '01-00-1980',
88 '01-13-1980',
89 '00-01-1980',
90 '32-01-1980',
91 '01-01-1980 25:00:00',
92 '01-01-1980 00:61:00',
93 '01-01-1980 00:00:62',
94 ]:
95 self.assert_(http2time(test) is None,
96 "http2time(%s) is not None\n"
97 "http2time(test) %s" % (test, http2time(test))
98 )
99
100
101class HeaderTests(TestCase):
Georg Brandlf9f33aa2010-05-22 11:32:59 +0000102
103 def test_parse_ns_headers_expires(self):
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000104 from cookielib import parse_ns_headers
105
106 # quotes should be stripped
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000107 expected = [[('foo', 'bar'), ('expires', 2209069412L), ('version', '0')]]
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000108 for hdr in [
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000109 'foo=bar; expires=01 Jan 2040 22:23:32 GMT',
110 'foo=bar; expires="01 Jan 2040 22:23:32 GMT"',
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000111 ]:
112 self.assertEquals(parse_ns_headers([hdr]), expected)
113
Georg Brandlf9f33aa2010-05-22 11:32:59 +0000114 def test_parse_ns_headers_version(self):
115 from cookielib import parse_ns_headers
116
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"',
122 ]:
123 self.assertEquals(parse_ns_headers([hdr]), expected)
124
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000125 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
129
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)
134
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000135 def test_join_header_words(self):
136 from cookielib import join_header_words
137
138 joined = join_header_words([[("foo", None), ("bar", "baz")]])
139 self.assertEquals(joined, "foo; bar=baz")
140
141 self.assertEquals(join_header_words([[]]), "")
142
143 def test_split_header_words(self):
144 from cookielib import split_header_words
145
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", "")]]),
163 ]
164
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, """
174When parsing: '%s'
175Expected: '%s'
176Got: '%s'
177""" % (arg, expect, result))
178
179 def test_roundtrip(self):
180 from cookielib import split_header_words, join_header_words
181
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'),
193
194 ('text/html; charset=iso-8859-1',
195 'text/html; charset="iso-8859-1"'),
196
197 ('foo="bar"; port="80,81"; discard, bar=baz',
198 'foo=bar; port="80,81"; discard, bar=baz'),
199
200 (r'Basic realm="\"foo\\\\bar\""',
201 r'Basic; realm="\"foo\\\\bar\""')
202 ]
203
204 for arg, expect in tests:
205 input = split_header_words([arg])
206 res = join_header_words(input)
207 self.assertEquals(res, expect, """
208When parsing: '%s'
209Expected: '%s'
210Got: '%s'
211Input was: '%s'
212""" % (arg, expect, res, input))
213
214
215class FakeResponse:
216 def __init__(self, headers=[], url=None):
217 """
218 headers: list of RFC822-style 'Key: value' strings
219 """
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
225
226def interact_2965(cookiejar, url, *set_cookie_hdrs):
227 return _interact(cookiejar, url, set_cookie_hdrs, "Set-Cookie2")
228
229def interact_netscape(cookiejar, url, *set_cookie_hdrs):
230 return _interact(cookiejar, url, set_cookie_hdrs, "Set-Cookie")
231
232def _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
244
245
Martin v. Löwisc5574e82005-03-03 10:57:37 +0000246class 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)
262
Neal Norwitz3e7de592005-12-23 21:24:35 +0000263 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
Martin v. Löwisc5574e82005-03-03 10:57:37 +0000288
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000289class 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.
312
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...
335
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),
361 ]:
362 request = urllib2.Request(url)
363 r = pol.domain_return_ok(domain, request)
364 if ok: self.assert_(r)
365 else: self.assert_(not r)
366
367 def test_missing_value(self):
368 from cookielib import MozillaCookieJar, lwp_cookie_str
369
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.assert_(cookie.value is None)
378 self.assertEquals(cookie.name, "eggs")
379 cookie = c._cookies["www.acme.com"]['/foo/']['"spam"']
380 self.assert_(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)
397 )
398 self.assertEquals(interact_netscape(c, "http://www.acme.com/foo/"),
399 '"spam"; eggs')
400
Neal Norwitz71dad722005-12-23 21:43:48 +0000401 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
405
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),
415 ]:
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.assert_(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)
433
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000434 def test_ns_parser(self):
435 from cookielib import CookieJar, DEFAULT_HTTP_PORT
436
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"')
445
446 cookie = c._cookies[".acme.com"]["/"]["spam"]
447 self.assertEquals(cookie.domain, ".acme.com")
448 self.assert_(cookie.domain_specified)
449 self.assertEquals(cookie.port, DEFAULT_HTTP_PORT)
450 self.assert_(not cookie.port_specified)
451 # case is preserved
452 self.assert_(cookie.has_nonstandard_attr("blArgh") and
453 not cookie.has_nonstandard_attr("blargh"))
454
455 cookie = c._cookies["www.acme.com"]["/"]["ni"]
456 self.assertEquals(cookie.domain, "www.acme.com")
457 self.assert_(not cookie.domain_specified)
458 self.assertEquals(cookie.port, "80,8080")
459 self.assert_(cookie.port_specified)
460
461 cookie = c._cookies["www.acme.com"]["/"]["nini"]
462 self.assert_(cookie.port is None)
463 self.assert_(not cookie.port_specified)
464
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.assert_(foo.expires is None)
469 self.assert_(spam.expires is None)
470
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000471 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
475
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')
479
480 cookies = c._cookies["www.acme.com"]["/"]
481 self.assert_('expires' in cookies)
482 self.assert_('version' in cookies)
483
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000484 def test_expires(self):
485 from cookielib import time2netscape, CookieJar
486
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.assert_('spam="bar"' in h and "foo" not in h)
500
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)
514
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.assert_('spam="bar"' in h)
521
522 # XXX RFC 2965 expiry rules (some apply to V0 too)
523
524 def test_default_path(self):
525 from cookielib import CookieJar, DefaultCookiePolicy
526
527 # RFC 2965
528 pol = DefaultCookiePolicy(rfc2965=True)
529
530 c = CookieJar(pol)
531 interact_2965(c, "http://www.acme.com/", 'spam="bar"; Version="1"')
532 self.assert_("/" in c._cookies["www.acme.com"])
533
534 c = CookieJar(pol)
535 interact_2965(c, "http://www.acme.com/blah", 'eggs="bar"; Version="1"')
536 self.assert_("/" in c._cookies["www.acme.com"])
537
538 c = CookieJar(pol)
539 interact_2965(c, "http://www.acme.com/blah/rhubarb",
540 'eggs="bar"; Version="1"')
541 self.assert_("/blah/" in 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.assert_("/blah/rhubarb/" in c._cookies["www.acme.com"])
547
548 # Netscape
549
550 c = CookieJar()
551 interact_netscape(c, "http://www.acme.com/", 'spam="bar"')
552 self.assert_("/" in c._cookies["www.acme.com"])
553
554 c = CookieJar()
555 interact_netscape(c, "http://www.acme.com/blah", 'eggs="bar"')
556 self.assert_("/" in c._cookies["www.acme.com"])
557
558 c = CookieJar()
559 interact_netscape(c, "http://www.acme.com/blah/rhubarb", 'eggs="bar"')
560 self.assert_("/blah" in c._cookies["www.acme.com"])
561
562 c = CookieJar()
563 interact_netscape(c, "http://www.acme.com/blah/rhubarb/", 'eggs="bar"')
564 self.assert_("/blah/rhubarb" in c._cookies["www.acme.com"])
565
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
586 ]
587 for arg, result in cases:
588 self.assertEquals(escape_path(arg), result)
589
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), "/")
606
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)
616
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")
639
640 def test_is_HDN(self):
641 from cookielib import is_HDN
642 self.assert_(is_HDN("foo.bar.com"))
643 self.assert_(is_HDN("1foo2.3bar4.5com"))
644 self.assert_(not is_HDN("192.168.1.1"))
645 self.assert_(not is_HDN(""))
646 self.assert_(not is_HDN("."))
647 self.assert_(not is_HDN(".foo.bar.com"))
648 self.assert_(not is_HDN("..foo"))
649 self.assert_(not is_HDN("foo."))
650
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")
661
662 def test_domain_match(self):
663 from cookielib import domain_match, user_domain_match
664 self.assert_(domain_match("192.168.1.1", "192.168.1.1"))
665 self.assert_(not domain_match("192.168.1.1", ".168.1.1"))
666 self.assert_(domain_match("x.y.com", "x.Y.com"))
667 self.assert_(domain_match("x.y.com", ".Y.com"))
668 self.assert_(not domain_match("x.y.com", "Y.com"))
669 self.assert_(domain_match("a.b.c.com", ".c.com"))
670 self.assert_(not domain_match(".c.com", "a.b.c.com"))
671 self.assert_(domain_match("example.local", ".local"))
672 self.assert_(not domain_match("blah.blah", ""))
673 self.assert_(not domain_match("", ".rhubarb.rhubarb"))
674 self.assert_(domain_match("", ""))
675
676 self.assert_(user_domain_match("acme.com", "acme.com"))
677 self.assert_(not user_domain_match("acme.com", ".acme.com"))
678 self.assert_(user_domain_match("rhubarb.acme.com", ".acme.com"))
679 self.assert_(user_domain_match("www.rhubarb.acme.com", ".acme.com"))
680 self.assert_(user_domain_match("x.y.com", "x.Y.com"))
681 self.assert_(user_domain_match("x.y.com", ".Y.com"))
682 self.assert_(not user_domain_match("x.y.com", "Y.com"))
683 self.assert_(user_domain_match("y.com", "Y.com"))
684 self.assert_(not user_domain_match(".y.com", "Y.com"))
685 self.assert_(user_domain_match(".y.com", ".Y.com"))
686 self.assert_(user_domain_match("x.y.com", ".com"))
687 self.assert_(not user_domain_match("x.y.com", "com"))
688 self.assert_(not user_domain_match("x.y.com", "m"))
689 self.assert_(not user_domain_match("x.y.com", ".m"))
690 self.assert_(not user_domain_match("x.y.com", ""))
691 self.assert_(not user_domain_match("x.y.com", "."))
692 self.assert_(user_domain_match("192.168.1.1", "192.168.1.1"))
693 # not both HDNs, so must string-compare equal to match
694 self.assert_(not user_domain_match("192.168.1.1", ".168.1.1"))
695 self.assert_(not user_domain_match("192.168.1.1", "."))
696 # empty string is a special case
697 self.assert_(not user_domain_match("192.168.1.1", ""))
698
699 def test_wrong_domain(self):
700 # Cookies whose effective request-host name does not domain-match the
701 # domain are rejected.
702
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)
709
Georg Brandla166a912006-05-08 17:28:47 +0000710 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
714
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)
725
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000726 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
731
732 c = CookieJar()
733
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/"), "")
747
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")
762
763 # top-level domain is too general
764 interact_netscape(c, "http://foo.net/", 'nini="ni"; domain=.net')
765 self.assertEquals(len(c), 3)
766
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)
775
776 def test_two_component_domain_rfc2965(self):
777 from cookielib import CookieJar, DefaultCookiePolicy
778
779 pol = DefaultCookiePolicy(rfc2965=True)
780 c = CookieJar(pol)
781
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/"), "")
790
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")
799
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"')
811
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)
816
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)
821
822 def test_domain_allow(self):
823 from cookielib import CookieJar, DefaultCookiePolicy
824 from urllib2 import Request
825
826 c = CookieJar(policy=DefaultCookiePolicy(
827 blocked_domains=["acme.com"],
828 allowed_domains=["www.acme.com"]))
829
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)
835
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)
840
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)
845
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.assert_(not req.has_header("Cookie"))
855
856 def test_domain_block(self):
857 from cookielib import CookieJar, DefaultCookiePolicy
858 from urllib2 import Request
859
860 pol = DefaultCookiePolicy(
861 rfc2965=True, blocked_domains=[".acme.com"])
862 c = CookieJar(policy=pol)
863 headers = ["Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/"]
864
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)
869
870 p = pol.set_blocked_domains(["acme.com"])
871 c.extract_cookies(res, req)
872 self.assertEquals(len(c), 1)
873
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.assert_((req.has_header("Cookie") and
882 req.has_header("Cookie2")))
883
884 c.clear()
885 pol.set_blocked_domains([".acme.com"])
886 c.extract_cookies(res, req)
887 self.assertEquals(len(c), 1)
888
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.assert_(not req.has_header("Cookie"))
898
899 def test_secure(self):
900 from cookielib import CookieJar, DefaultCookiePolicy
901
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.assert_(
918 not c._cookies["www.acme.com"]["/"]["foo1"].secure,
919 "non-secure cookie registered secure")
920 self.assert_(
921 c._cookies["www.acme.com"]["/"]["foo2"].secure,
922 "secure cookie registered non-secure")
923
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')
930
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.assert_(req.has_header("Cookie"))
942
943 def test_domain_mirror(self):
944 from cookielib import CookieJar, DefaultCookiePolicy
945
946 pol = DefaultCookiePolicy(rfc2965=True)
947
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.assert_("Domain" not in h,
953 "absent domain returned with domain present")
954
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.assert_('$Domain=".bar.com"' in h, "domain not returned")
960
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.assert_('$Domain="bar.com"' in h, "domain not returned")
967
968 def test_path_mirror(self):
969 from cookielib import CookieJar, DefaultCookiePolicy
970
971 pol = DefaultCookiePolicy(rfc2965=True)
972
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.assert_("Path" not in h,
978 "absent path returned with path present")
979
980 c = CookieJar(pol)
981 url = "http://foo.bar.com/"
982 interact_2965(c, url, 'spam=eggs; Version=1; Path=/')
983 h = interact_2965(c, url)
984 self.assert_('$Path="/"' in h, "path not returned")
985
986 def test_port_mirror(self):
987 from cookielib import CookieJar, DefaultCookiePolicy
988
989 pol = DefaultCookiePolicy(rfc2965=True)
990
991 c = CookieJar(pol)
992 url = "http://foo.bar.com/"
993 interact_2965(c, url, "spam=eggs; Version=1")
994 h = interact_2965(c, url)
995 self.assert_("Port" not in h,
996 "absent port returned with port present")
997
998 c = CookieJar(pol)
999 url = "http://foo.bar.com/"
1000 interact_2965(c, url, "spam=eggs; Version=1; Port")
1001 h = interact_2965(c, url)
1002 self.assert_(re.search("\$Port([^=]|$)", h),
1003 "port with no value not returned with no value")
1004
1005 c = CookieJar(pol)
1006 url = "http://foo.bar.com/"
1007 interact_2965(c, url, 'spam=eggs; Version=1; Port="80"')
1008 h = interact_2965(c, url)
1009 self.assert_('$Port="80"' in h,
1010 "port with single value not returned with single value")
1011
1012 c = CookieJar(pol)
1013 url = "http://foo.bar.com/"
1014 interact_2965(c, url, 'spam=eggs; Version=1; Port="80,8080"')
1015 h = interact_2965(c, url)
1016 self.assert_('$Port="80,8080"' in h,
1017 "port with multiple values not returned with multiple "
1018 "values")
1019
1020 def test_no_return_comment(self):
1021 from cookielib import CookieJar, DefaultCookiePolicy
1022
1023 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1024 url = "http://foo.bar.com/"
1025 interact_2965(c, url, 'spam=eggs; Version=1; '
1026 'Comment="does anybody read these?"; '
1027 'CommentURL="http://foo.bar.net/comment.html"')
1028 h = interact_2965(c, url)
1029 self.assert_(
1030 "Comment" not in h,
1031 "Comment or CommentURL cookie-attributes returned to server")
1032
1033 def test_Cookie_iterator(self):
1034 from cookielib import CookieJar, Cookie, DefaultCookiePolicy
1035
1036 cs = CookieJar(DefaultCookiePolicy(rfc2965=True))
1037 # add some random cookies
1038 interact_2965(cs, "http://blah.spam.org/", 'foo=eggs; Version=1; '
1039 'Comment="does anybody read these?"; '
1040 'CommentURL="http://foo.bar.net/comment.html"')
1041 interact_netscape(cs, "http://www.acme.com/blah/", "spam=bar; secure")
1042 interact_2965(cs, "http://www.acme.com/blah/",
1043 "foo=bar; secure; Version=1")
1044 interact_2965(cs, "http://www.acme.com/blah/",
1045 "foo=bar; path=/; Version=1")
1046 interact_2965(cs, "http://www.sol.no",
1047 r'bang=wallop; version=1; domain=".sol.no"; '
1048 r'port="90,100, 80,8080"; '
1049 r'max-age=100; Comment = "Just kidding! (\"|\\\\) "')
1050
1051 versions = [1, 1, 1, 0, 1]
1052 names = ["bang", "foo", "foo", "spam", "foo"]
1053 domains = [".sol.no", "blah.spam.org", "www.acme.com",
1054 "www.acme.com", "www.acme.com"]
1055 paths = ["/", "/", "/", "/blah", "/blah/"]
1056
1057 for i in range(4):
1058 i = 0
1059 for c in cs:
1060 self.assert_(isinstance(c, Cookie))
1061 self.assertEquals(c.version, versions[i])
1062 self.assertEquals(c.name, names[i])
1063 self.assertEquals(c.domain, domains[i])
1064 self.assertEquals(c.path, paths[i])
1065 i = i + 1
1066
1067 def test_parse_ns_headers(self):
1068 from cookielib import parse_ns_headers
1069
1070 # missing domain value (invalid cookie)
1071 self.assertEquals(
1072 parse_ns_headers(["foo=bar; path=/; domain"]),
1073 [[("foo", "bar"),
1074 ("path", "/"), ("domain", None), ("version", "0")]]
1075 )
1076 # invalid expires value
1077 self.assertEquals(
1078 parse_ns_headers(["foo=bar; expires=Foo Bar 12 33:22:11 2000"]),
1079 [[("foo", "bar"), ("expires", None), ("version", "0")]]
1080 )
1081 # missing cookie value (valid cookie)
1082 self.assertEquals(
1083 parse_ns_headers(["foo"]),
1084 [[("foo", None), ("version", "0")]]
1085 )
1086 # shouldn't add version if header is empty
1087 self.assertEquals(parse_ns_headers([""]), [])
1088
1089 def test_bad_cookie_header(self):
1090
1091 def cookiejar_from_cookie_headers(headers):
1092 from cookielib import CookieJar
1093 from urllib2 import Request
1094 c = CookieJar()
1095 req = Request("http://www.example.com/")
1096 r = FakeResponse(headers, "http://www.example.com/")
1097 c.extract_cookies(r, req)
1098 return c
1099
1100 # none of these bad headers should cause an exception to be raised
1101 for headers in [
1102 ["Set-Cookie: "], # actually, nothing wrong with this
1103 ["Set-Cookie2: "], # ditto
1104 # missing domain value
1105 ["Set-Cookie2: a=foo; path=/; Version=1; domain"],
1106 # bad max-age
1107 ["Set-Cookie: b=foo; max-age=oops"],
Georg Brandlf9f33aa2010-05-22 11:32:59 +00001108 # bad version
1109 ["Set-Cookie: b=foo; version=spam"],
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001110 ]:
1111 c = cookiejar_from_cookie_headers(headers)
1112 # these bad cookies shouldn't be set
1113 self.assertEquals(len(c), 0)
1114
1115 # cookie with invalid expires is treated as session cookie
1116 headers = ["Set-Cookie: c=foo; expires=Foo Bar 12 33:22:11 2000"]
1117 c = cookiejar_from_cookie_headers(headers)
1118 cookie = c._cookies["www.example.com"]["/"]["c"]
1119 self.assert_(cookie.expires is None)
1120
1121
1122class LWPCookieTests(TestCase):
1123 # Tests taken from libwww-perl, with a few modifications and additions.
1124
1125 def test_netscape_example_1(self):
1126 from cookielib import CookieJar, DefaultCookiePolicy
1127 from urllib2 import Request
1128
1129 #-------------------------------------------------------------------
1130 # First we check that it works for the original example at
1131 # http://www.netscape.com/newsref/std/cookie_spec.html
1132
1133 # Client requests a document, and receives in the response:
1134 #
1135 # Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT
1136 #
1137 # When client requests a URL in path "/" on this server, it sends:
1138 #
1139 # Cookie: CUSTOMER=WILE_E_COYOTE
1140 #
1141 # Client requests a document, and receives in the response:
1142 #
1143 # Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
1144 #
1145 # When client requests a URL in path "/" on this server, it sends:
1146 #
1147 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
1148 #
1149 # Client receives:
1150 #
1151 # Set-Cookie: SHIPPING=FEDEX; path=/fo
1152 #
1153 # When client requests a URL in path "/" on this server, it sends:
1154 #
1155 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
1156 #
1157 # When client requests a URL in path "/foo" on this server, it sends:
1158 #
1159 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX
1160 #
1161 # The last Cookie is buggy, because both specifications say that the
1162 # most specific cookie must be sent first. SHIPPING=FEDEX is the
1163 # most specific and should thus be first.
1164
1165 year_plus_one = time.localtime()[0] + 1
1166
1167 headers = []
1168
1169 c = CookieJar(DefaultCookiePolicy(rfc2965 = True))
1170
1171 #req = Request("http://1.1.1.1/",
1172 # headers={"Host": "www.acme.com:80"})
1173 req = Request("http://www.acme.com:80/",
1174 headers={"Host": "www.acme.com:80"})
1175
1176 headers.append(
1177 "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/ ; "
1178 "expires=Wednesday, 09-Nov-%d 23:12:40 GMT" % year_plus_one)
1179 res = FakeResponse(headers, "http://www.acme.com/")
1180 c.extract_cookies(res, req)
1181
1182 req = Request("http://www.acme.com/")
1183 c.add_cookie_header(req)
1184
1185 self.assertEqual(req.get_header("Cookie"), "CUSTOMER=WILE_E_COYOTE")
1186 self.assertEqual(req.get_header("Cookie2"), '$Version="1"')
1187
1188 headers.append("Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/")
1189 res = FakeResponse(headers, "http://www.acme.com/")
1190 c.extract_cookies(res, req)
1191
1192 req = Request("http://www.acme.com/foo/bar")
1193 c.add_cookie_header(req)
1194
1195 h = req.get_header("Cookie")
1196 self.assert_("PART_NUMBER=ROCKET_LAUNCHER_0001" in h and
1197 "CUSTOMER=WILE_E_COYOTE" in h)
1198
1199 headers.append('Set-Cookie: SHIPPING=FEDEX; path=/foo')
1200 res = FakeResponse(headers, "http://www.acme.com")
1201 c.extract_cookies(res, req)
1202
1203 req = Request("http://www.acme.com/")
1204 c.add_cookie_header(req)
1205
1206 h = req.get_header("Cookie")
1207 self.assert_("PART_NUMBER=ROCKET_LAUNCHER_0001" in h and
1208 "CUSTOMER=WILE_E_COYOTE" in h and
1209 "SHIPPING=FEDEX" not in h)
1210
1211 req = Request("http://www.acme.com/foo/")
1212 c.add_cookie_header(req)
1213
1214 h = req.get_header("Cookie")
1215 self.assert_(("PART_NUMBER=ROCKET_LAUNCHER_0001" in h and
1216 "CUSTOMER=WILE_E_COYOTE" in h and
1217 h.startswith("SHIPPING=FEDEX;")))
1218
1219 def test_netscape_example_2(self):
1220 from cookielib import CookieJar
1221 from urllib2 import Request
1222
1223 # Second Example transaction sequence:
1224 #
1225 # Assume all mappings from above have been cleared.
1226 #
1227 # Client receives:
1228 #
1229 # Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
1230 #
1231 # When client requests a URL in path "/" on this server, it sends:
1232 #
1233 # Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001
1234 #
1235 # Client receives:
1236 #
1237 # Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo
1238 #
1239 # When client requests a URL in path "/ammo" on this server, it sends:
1240 #
1241 # Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001
1242 #
1243 # NOTE: There are two name/value pairs named "PART_NUMBER" due to
1244 # the inheritance of the "/" mapping in addition to the "/ammo" mapping.
1245
1246 c = CookieJar()
1247 headers = []
1248
1249 req = Request("http://www.acme.com/")
1250 headers.append("Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/")
1251 res = FakeResponse(headers, "http://www.acme.com/")
1252
1253 c.extract_cookies(res, req)
1254
1255 req = Request("http://www.acme.com/")
1256 c.add_cookie_header(req)
1257
1258 self.assertEquals(req.get_header("Cookie"),
1259 "PART_NUMBER=ROCKET_LAUNCHER_0001")
1260
1261 headers.append(
1262 "Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo")
1263 res = FakeResponse(headers, "http://www.acme.com/")
1264 c.extract_cookies(res, req)
1265
1266 req = Request("http://www.acme.com/ammo")
1267 c.add_cookie_header(req)
1268
1269 self.assert_(re.search(r"PART_NUMBER=RIDING_ROCKET_0023;\s*"
1270 "PART_NUMBER=ROCKET_LAUNCHER_0001",
1271 req.get_header("Cookie")))
1272
1273 def test_ietf_example_1(self):
1274 from cookielib import CookieJar, DefaultCookiePolicy
1275 #-------------------------------------------------------------------
1276 # Then we test with the examples from draft-ietf-http-state-man-mec-03.txt
1277 #
1278 # 5. EXAMPLES
1279
1280 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1281
1282 #
1283 # 5.1 Example 1
1284 #
1285 # Most detail of request and response headers has been omitted. Assume
1286 # the user agent has no stored cookies.
1287 #
1288 # 1. User Agent -> Server
1289 #
1290 # POST /acme/login HTTP/1.1
1291 # [form data]
1292 #
1293 # User identifies self via a form.
1294 #
1295 # 2. Server -> User Agent
1296 #
1297 # HTTP/1.1 200 OK
1298 # Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
1299 #
1300 # Cookie reflects user's identity.
1301
1302 cookie = interact_2965(
1303 c, 'http://www.acme.com/acme/login',
1304 'Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"')
1305 self.assert_(not cookie)
1306
1307 #
1308 # 3. User Agent -> Server
1309 #
1310 # POST /acme/pickitem HTTP/1.1
1311 # Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
1312 # [form data]
1313 #
1314 # User selects an item for ``shopping basket.''
1315 #
1316 # 4. Server -> User Agent
1317 #
1318 # HTTP/1.1 200 OK
1319 # Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
1320 # Path="/acme"
1321 #
1322 # Shopping basket contains an item.
1323
1324 cookie = interact_2965(c, 'http://www.acme.com/acme/pickitem',
1325 'Part_Number="Rocket_Launcher_0001"; '
1326 'Version="1"; Path="/acme"');
1327 self.assert_(re.search(
1328 r'^\$Version="?1"?; Customer="?WILE_E_COYOTE"?; \$Path="/acme"$',
1329 cookie))
1330
1331 #
1332 # 5. User Agent -> Server
1333 #
1334 # POST /acme/shipping HTTP/1.1
1335 # Cookie: $Version="1";
1336 # Customer="WILE_E_COYOTE"; $Path="/acme";
1337 # Part_Number="Rocket_Launcher_0001"; $Path="/acme"
1338 # [form data]
1339 #
1340 # User selects shipping method from form.
1341 #
1342 # 6. Server -> User Agent
1343 #
1344 # HTTP/1.1 200 OK
1345 # Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"
1346 #
1347 # New cookie reflects shipping method.
1348
1349 cookie = interact_2965(c, "http://www.acme.com/acme/shipping",
1350 'Shipping="FedEx"; Version="1"; Path="/acme"')
1351
1352 self.assert_(re.search(r'^\$Version="?1"?;', cookie))
1353 self.assert_(re.search(r'Part_Number="?Rocket_Launcher_0001"?;'
1354 '\s*\$Path="\/acme"', cookie))
1355 self.assert_(re.search(r'Customer="?WILE_E_COYOTE"?;\s*\$Path="\/acme"',
1356 cookie))
1357
1358 #
1359 # 7. User Agent -> Server
1360 #
1361 # POST /acme/process HTTP/1.1
1362 # Cookie: $Version="1";
1363 # Customer="WILE_E_COYOTE"; $Path="/acme";
1364 # Part_Number="Rocket_Launcher_0001"; $Path="/acme";
1365 # Shipping="FedEx"; $Path="/acme"
1366 # [form data]
1367 #
1368 # User chooses to process order.
1369 #
1370 # 8. Server -> User Agent
1371 #
1372 # HTTP/1.1 200 OK
1373 #
1374 # Transaction is complete.
1375
1376 cookie = interact_2965(c, "http://www.acme.com/acme/process")
1377 self.assert_(
1378 re.search(r'Shipping="?FedEx"?;\s*\$Path="\/acme"', cookie) and
1379 "WILE_E_COYOTE" in cookie)
1380
1381 #
1382 # The user agent makes a series of requests on the origin server, after
1383 # each of which it receives a new cookie. All the cookies have the same
1384 # Path attribute and (default) domain. Because the request URLs all have
1385 # /acme as a prefix, and that matches the Path attribute, each request
1386 # contains all the cookies received so far.
1387
1388 def test_ietf_example_2(self):
1389 from cookielib import CookieJar, DefaultCookiePolicy
1390
1391 # 5.2 Example 2
1392 #
1393 # This example illustrates the effect of the Path attribute. All detail
1394 # of request and response headers has been omitted. Assume the user agent
1395 # has no stored cookies.
1396
1397 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1398
1399 # Imagine the user agent has received, in response to earlier requests,
1400 # the response headers
1401 #
1402 # Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
1403 # Path="/acme"
1404 #
1405 # and
1406 #
1407 # Set-Cookie2: Part_Number="Riding_Rocket_0023"; Version="1";
1408 # Path="/acme/ammo"
1409
1410 interact_2965(
1411 c, "http://www.acme.com/acme/ammo/specific",
1412 'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"',
1413 'Part_Number="Riding_Rocket_0023"; Version="1"; Path="/acme/ammo"')
1414
1415 # A subsequent request by the user agent to the (same) server for URLs of
1416 # the form /acme/ammo/... would include the following request header:
1417 #
1418 # Cookie: $Version="1";
1419 # Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
1420 # Part_Number="Rocket_Launcher_0001"; $Path="/acme"
1421 #
1422 # Note that the NAME=VALUE pair for the cookie with the more specific Path
1423 # attribute, /acme/ammo, comes before the one with the less specific Path
1424 # attribute, /acme. Further note that the same cookie name appears more
1425 # than once.
1426
1427 cookie = interact_2965(c, "http://www.acme.com/acme/ammo/...")
1428 self.assert_(
1429 re.search(r"Riding_Rocket_0023.*Rocket_Launcher_0001", cookie))
1430
1431 # A subsequent request by the user agent to the (same) server for a URL of
1432 # the form /acme/parts/ would include the following request header:
1433 #
1434 # Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"
1435 #
1436 # Here, the second cookie's Path attribute /acme/ammo is not a prefix of
1437 # the request URL, /acme/parts/, so the cookie does not get forwarded to
1438 # the server.
1439
1440 cookie = interact_2965(c, "http://www.acme.com/acme/parts/")
1441 self.assert_("Rocket_Launcher_0001" in cookie and
1442 "Riding_Rocket_0023" not in cookie)
1443
1444 def test_rejection(self):
1445 # Test rejection of Set-Cookie2 responses based on domain, path, port.
1446 from cookielib import DefaultCookiePolicy, LWPCookieJar
1447
1448 pol = DefaultCookiePolicy(rfc2965=True)
1449
1450 c = LWPCookieJar(policy=pol)
1451
1452 max_age = "max-age=3600"
1453
1454 # illegal domain (no embedded dots)
1455 cookie = interact_2965(c, "http://www.acme.com",
1456 'foo=bar; domain=".com"; version=1')
1457 self.assert_(not c)
1458
1459 # legal domain
1460 cookie = interact_2965(c, "http://www.acme.com",
1461 'ping=pong; domain="acme.com"; version=1')
1462 self.assertEquals(len(c), 1)
1463
1464 # illegal domain (host prefix "www.a" contains a dot)
1465 cookie = interact_2965(c, "http://www.a.acme.com",
1466 'whiz=bang; domain="acme.com"; version=1')
1467 self.assertEquals(len(c), 1)
1468
1469 # legal domain
1470 cookie = interact_2965(c, "http://www.a.acme.com",
1471 'wow=flutter; domain=".a.acme.com"; version=1')
1472 self.assertEquals(len(c), 2)
1473
1474 # can't partially match an IP-address
1475 cookie = interact_2965(c, "http://125.125.125.125",
1476 'zzzz=ping; domain="125.125.125"; version=1')
1477 self.assertEquals(len(c), 2)
1478
1479 # illegal path (must be prefix of request path)
1480 cookie = interact_2965(c, "http://www.sol.no",
1481 'blah=rhubarb; domain=".sol.no"; path="/foo"; '
1482 'version=1')
1483 self.assertEquals(len(c), 2)
1484
1485 # legal path
1486 cookie = interact_2965(c, "http://www.sol.no/foo/bar",
1487 'bing=bong; domain=".sol.no"; path="/foo"; '
1488 'version=1')
1489 self.assertEquals(len(c), 3)
1490
1491 # illegal port (request-port not in list)
1492 cookie = interact_2965(c, "http://www.sol.no",
1493 'whiz=ffft; domain=".sol.no"; port="90,100"; '
1494 'version=1')
1495 self.assertEquals(len(c), 3)
1496
1497 # legal port
1498 cookie = interact_2965(
1499 c, "http://www.sol.no",
1500 r'bang=wallop; version=1; domain=".sol.no"; '
1501 r'port="90,100, 80,8080"; '
1502 r'max-age=100; Comment = "Just kidding! (\"|\\\\) "')
1503 self.assertEquals(len(c), 4)
1504
1505 # port attribute without any value (current port)
1506 cookie = interact_2965(c, "http://www.sol.no",
1507 'foo9=bar; version=1; domain=".sol.no"; port; '
1508 'max-age=100;')
1509 self.assertEquals(len(c), 5)
1510
1511 # encoded path
1512 # LWP has this test, but unescaping allowed path characters seems
1513 # like a bad idea, so I think this should fail:
1514## cookie = interact_2965(c, "http://www.sol.no/foo/",
1515## r'foo8=bar; version=1; path="/%66oo"')
1516 # but this is OK, because '<' is not an allowed HTTP URL path
1517 # character:
1518 cookie = interact_2965(c, "http://www.sol.no/<oo/",
1519 r'foo8=bar; version=1; path="/%3coo"')
1520 self.assertEquals(len(c), 6)
1521
1522 # save and restore
1523 filename = test_support.TESTFN
1524
1525 try:
1526 c.save(filename, ignore_discard=True)
1527 old = repr(c)
1528
1529 c = LWPCookieJar(policy=pol)
1530 c.load(filename, ignore_discard=True)
1531 finally:
1532 try: os.unlink(filename)
1533 except OSError: pass
1534
1535 self.assertEquals(old, repr(c))
1536
1537 def test_url_encoding(self):
1538 # Try some URL encodings of the PATHs.
1539 # (the behaviour here has changed from libwww-perl)
1540 from cookielib import CookieJar, DefaultCookiePolicy
1541
1542 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1543 interact_2965(c, "http://www.acme.com/foo%2f%25/%3c%3c%0Anew%E5/%E5",
1544 "foo = bar; version = 1")
1545
1546 cookie = interact_2965(
Brett Cannon0bfac6e2008-07-02 21:52:42 +00001547 c, "http://www.acme.com/foo%2f%25/<<%0anewå/æøå",
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001548 'bar=baz; path="/foo/"; version=1');
1549 version_re = re.compile(r'^\$version=\"?1\"?', re.I)
1550 self.assert_("foo=bar" in cookie and version_re.search(cookie))
1551
1552 cookie = interact_2965(
Brett Cannon0bfac6e2008-07-02 21:52:42 +00001553 c, "http://www.acme.com/foo/%25/<<%0anewå/æøå")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001554 self.assert_(not cookie)
1555
1556 # unicode URL doesn't raise exception
1557 cookie = interact_2965(c, u"http://www.acme.com/\xfc")
1558
1559 def test_mozilla(self):
1560 # Save / load Mozilla/Netscape cookie file format.
1561 from cookielib import MozillaCookieJar, DefaultCookiePolicy
1562
1563 year_plus_one = time.localtime()[0] + 1
1564
1565 filename = test_support.TESTFN
1566
1567 c = MozillaCookieJar(filename,
1568 policy=DefaultCookiePolicy(rfc2965=True))
1569 interact_2965(c, "http://www.acme.com/",
1570 "foo1=bar; max-age=100; Version=1")
1571 interact_2965(c, "http://www.acme.com/",
1572 'foo2=bar; port="80"; max-age=100; Discard; Version=1')
1573 interact_2965(c, "http://www.acme.com/", "foo3=bar; secure; Version=1")
1574
1575 expires = "expires=09-Nov-%d 23:12:40 GMT" % (year_plus_one,)
1576 interact_netscape(c, "http://www.foo.com/",
1577 "fooa=bar; %s" % expires)
1578 interact_netscape(c, "http://www.foo.com/",
1579 "foob=bar; Domain=.foo.com; %s" % expires)
1580 interact_netscape(c, "http://www.foo.com/",
1581 "fooc=bar; Domain=www.foo.com; %s" % expires)
1582
1583 def save_and_restore(cj, ignore_discard):
1584 try:
1585 cj.save(ignore_discard=ignore_discard)
1586 new_c = MozillaCookieJar(filename,
1587 DefaultCookiePolicy(rfc2965=True))
1588 new_c.load(ignore_discard=ignore_discard)
1589 finally:
1590 try: os.unlink(filename)
1591 except OSError: pass
1592 return new_c
1593
1594 new_c = save_and_restore(c, True)
1595 self.assertEquals(len(new_c), 6) # none discarded
1596 self.assert_("name='foo1', value='bar'" in repr(new_c))
1597
1598 new_c = save_and_restore(c, False)
1599 self.assertEquals(len(new_c), 4) # 2 of them discarded on save
1600 self.assert_("name='foo1', value='bar'" in repr(new_c))
1601
1602 def test_netscape_misc(self):
1603 # Some additional Netscape cookies tests.
1604 from cookielib import CookieJar
1605 from urllib2 import Request
1606
1607 c = CookieJar()
1608 headers = []
1609 req = Request("http://foo.bar.acme.com/foo")
1610
1611 # Netscape allows a host part that contains dots
1612 headers.append("Set-Cookie: Customer=WILE_E_COYOTE; domain=.acme.com")
1613 res = FakeResponse(headers, "http://www.acme.com/foo")
1614 c.extract_cookies(res, req)
1615
1616 # and that the domain is the same as the host without adding a leading
1617 # dot to the domain. Should not quote even if strange chars are used
1618 # in the cookie value.
1619 headers.append("Set-Cookie: PART_NUMBER=3,4; domain=foo.bar.acme.com")
1620 res = FakeResponse(headers, "http://www.acme.com/foo")
1621 c.extract_cookies(res, req)
1622
1623 req = Request("http://foo.bar.acme.com/foo")
1624 c.add_cookie_header(req)
1625 self.assert_(
1626 "PART_NUMBER=3,4" in req.get_header("Cookie") and
1627 "Customer=WILE_E_COYOTE" in req.get_header("Cookie"))
1628
1629 def test_intranet_domains_2965(self):
1630 # Test handling of local intranet hostnames without a dot.
1631 from cookielib import CookieJar, DefaultCookiePolicy
1632
1633 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1634 interact_2965(c, "http://example/",
1635 "foo1=bar; PORT; Discard; Version=1;")
1636 cookie = interact_2965(c, "http://example/",
1637 'foo2=bar; domain=".local"; Version=1')
1638 self.assert_("foo1=bar" in cookie)
1639
1640 interact_2965(c, "http://example/", 'foo3=bar; Version=1')
1641 cookie = interact_2965(c, "http://example/")
1642 self.assert_("foo2=bar" in cookie and len(c) == 3)
1643
1644 def test_intranet_domains_ns(self):
1645 from cookielib import CookieJar, DefaultCookiePolicy
1646
1647 c = CookieJar(DefaultCookiePolicy(rfc2965 = False))
1648 interact_netscape(c, "http://example/", "foo1=bar")
1649 cookie = interact_netscape(c, "http://example/",
1650 'foo2=bar; domain=.local')
1651 self.assertEquals(len(c), 2)
1652 self.assert_("foo1=bar" in cookie)
1653
1654 cookie = interact_netscape(c, "http://example/")
1655 self.assert_("foo2=bar" in cookie)
1656 self.assertEquals(len(c), 2)
1657
1658 def test_empty_path(self):
1659 from cookielib import CookieJar, DefaultCookiePolicy
1660 from urllib2 import Request
1661
1662 # Test for empty path
1663 # Broken web-server ORION/1.3.38 returns to the client response like
1664 #
1665 # Set-Cookie: JSESSIONID=ABCDERANDOM123; Path=
1666 #
1667 # ie. with Path set to nothing.
1668 # In this case, extract_cookies() must set cookie to / (root)
1669 c = CookieJar(DefaultCookiePolicy(rfc2965 = True))
1670 headers = []
1671
1672 req = Request("http://www.ants.com/")
1673 headers.append("Set-Cookie: JSESSIONID=ABCDERANDOM123; Path=")
1674 res = FakeResponse(headers, "http://www.ants.com/")
1675 c.extract_cookies(res, req)
1676
1677 req = Request("http://www.ants.com/")
1678 c.add_cookie_header(req)
1679
1680 self.assertEquals(req.get_header("Cookie"),
1681 "JSESSIONID=ABCDERANDOM123")
1682 self.assertEquals(req.get_header("Cookie2"), '$Version="1"')
1683
1684 # missing path in the request URI
1685 req = Request("http://www.ants.com:8080")
1686 c.add_cookie_header(req)
1687
1688 self.assertEquals(req.get_header("Cookie"),
1689 "JSESSIONID=ABCDERANDOM123")
1690 self.assertEquals(req.get_header("Cookie2"), '$Version="1"')
1691
1692 def test_session_cookies(self):
1693 from cookielib import CookieJar
1694 from urllib2 import Request
1695
1696 year_plus_one = time.localtime()[0] + 1
1697
1698 # Check session cookies are deleted properly by
1699 # CookieJar.clear_session_cookies method
1700
1701 req = Request('http://www.perlmeister.com/scripts')
1702 headers = []
1703 headers.append("Set-Cookie: s1=session;Path=/scripts")
1704 headers.append("Set-Cookie: p1=perm; Domain=.perlmeister.com;"
1705 "Path=/;expires=Fri, 02-Feb-%d 23:24:20 GMT" %
1706 year_plus_one)
1707 headers.append("Set-Cookie: p2=perm;Path=/;expires=Fri, "
1708 "02-Feb-%d 23:24:20 GMT" % year_plus_one)
1709 headers.append("Set-Cookie: s2=session;Path=/scripts;"
1710 "Domain=.perlmeister.com")
1711 headers.append('Set-Cookie2: s3=session;Version=1;Discard;Path="/"')
1712 res = FakeResponse(headers, 'http://www.perlmeister.com/scripts')
1713
1714 c = CookieJar()
1715 c.extract_cookies(res, req)
1716 # How many session/permanent cookies do we have?
1717 counter = {"session_after": 0,
1718 "perm_after": 0,
1719 "session_before": 0,
1720 "perm_before": 0}
1721 for cookie in c:
1722 key = "%s_before" % cookie.value
1723 counter[key] = counter[key] + 1
1724 c.clear_session_cookies()
1725 # How many now?
1726 for cookie in c:
1727 key = "%s_after" % cookie.value
1728 counter[key] = counter[key] + 1
1729
1730 self.assert_(not (
Jesus Cea585ad8a2009-07-02 15:37:21 +00001731 # a permanent cookie got lost accidentally
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001732 counter["perm_after"] != counter["perm_before"] or
1733 # a session cookie hasn't been cleared
1734 counter["session_after"] != 0 or
1735 # we didn't have session cookies in the first place
1736 counter["session_before"] == 0))
1737
1738
1739def test_main(verbose=None):
1740 from test import test_sets
1741 test_support.run_unittest(
1742 DateTimeTests,
1743 HeaderTests,
1744 CookieTests,
Martin v. Löwisc5574e82005-03-03 10:57:37 +00001745 FileCookieJarTests,
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001746 LWPCookieTests,
1747 )
1748
1749if __name__ == "__main__":
1750 test_main(verbose=True)