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