blob: b75511ce4768bb50f4d81861158343b0375fd32a [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):
Benjamin Peterson5c8da862009-06-30 22:57:08 +000024 self.assertTrue(re.search(r"^\d{4}-\d\d-\d\d \d\d:\d\d:\d\dZ$", text),
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000025 "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
Benjamin Peterson5c8da862009-06-30 22:57:08 +000077 self.assertTrue(t == t2 == t3 == test_t,
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000078 "'%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 ]:
Benjamin Peterson5c8da862009-06-30 22:57:08 +000095 self.assertTrue(http2time(test) is None,
Martin v. Löwis2a6ba902004-05-31 18:22:40 +000096 "http2time(%s) is not None\n"
97 "http2time(test) %s" % (test, http2time(test))
98 )
99
100
101class HeaderTests(TestCase):
102 def test_parse_ns_headers(self):
103 from cookielib import parse_ns_headers
104
105 # quotes should be stripped
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000106 expected = [[('foo', 'bar'), ('expires', 2209069412L), ('version', '0')]]
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000107 for hdr in [
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000108 'foo=bar; expires=01 Jan 2040 22:23:32 GMT',
109 'foo=bar; expires="01 Jan 2040 22:23:32 GMT"',
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000110 ]:
111 self.assertEquals(parse_ns_headers([hdr]), expected)
112
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000113 def test_parse_ns_headers_special_names(self):
114 # names such as 'expires' are not special in first name=value pair
115 # of Set-Cookie: header
116 from cookielib import parse_ns_headers
117
118 # Cookie with name 'expires'
119 hdr = 'expires=01 Jan 2040 22:23:32 GMT'
120 expected = [[("expires", "01 Jan 2040 22:23:32 GMT"), ("version", "0")]]
121 self.assertEquals(parse_ns_headers([hdr]), expected)
122
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000123 def test_join_header_words(self):
124 from cookielib import join_header_words
125
126 joined = join_header_words([[("foo", None), ("bar", "baz")]])
127 self.assertEquals(joined, "foo; bar=baz")
128
129 self.assertEquals(join_header_words([[]]), "")
130
131 def test_split_header_words(self):
132 from cookielib import split_header_words
133
134 tests = [
135 ("foo", [[("foo", None)]]),
136 ("foo=bar", [[("foo", "bar")]]),
137 (" foo ", [[("foo", None)]]),
138 (" foo= ", [[("foo", "")]]),
139 (" foo=", [[("foo", "")]]),
140 (" foo= ; ", [[("foo", "")]]),
141 (" foo= ; bar= baz ", [[("foo", ""), ("bar", "baz")]]),
142 ("foo=bar bar=baz", [[("foo", "bar"), ("bar", "baz")]]),
143 # doesn't really matter if this next fails, but it works ATM
144 ("foo= bar=baz", [[("foo", "bar=baz")]]),
145 ("foo=bar;bar=baz", [[("foo", "bar"), ("bar", "baz")]]),
146 ('foo bar baz', [[("foo", None), ("bar", None), ("baz", None)]]),
147 ("a, b, c", [[("a", None)], [("b", None)], [("c", None)]]),
148 (r'foo; bar=baz, spam=, foo="\,\;\"", bar= ',
149 [[("foo", None), ("bar", "baz")],
150 [("spam", "")], [("foo", ',;"')], [("bar", "")]]),
151 ]
152
153 for arg, expect in tests:
154 try:
155 result = split_header_words([arg])
156 except:
157 import traceback, StringIO
158 f = StringIO.StringIO()
159 traceback.print_exc(None, f)
160 result = "(error -- traceback follows)\n\n%s" % f.getvalue()
161 self.assertEquals(result, expect, """
162When parsing: '%s'
163Expected: '%s'
164Got: '%s'
165""" % (arg, expect, result))
166
167 def test_roundtrip(self):
168 from cookielib import split_header_words, join_header_words
169
170 tests = [
171 ("foo", "foo"),
172 ("foo=bar", "foo=bar"),
173 (" foo ", "foo"),
174 ("foo=", 'foo=""'),
175 ("foo=bar bar=baz", "foo=bar; bar=baz"),
176 ("foo=bar;bar=baz", "foo=bar; bar=baz"),
177 ('foo bar baz', "foo; bar; baz"),
178 (r'foo="\"" bar="\\"', r'foo="\""; bar="\\"'),
179 ('foo,,,bar', 'foo, bar'),
180 ('foo=bar,bar=baz', 'foo=bar, bar=baz'),
181
182 ('text/html; charset=iso-8859-1',
183 'text/html; charset="iso-8859-1"'),
184
185 ('foo="bar"; port="80,81"; discard, bar=baz',
186 'foo=bar; port="80,81"; discard, bar=baz'),
187
188 (r'Basic realm="\"foo\\\\bar\""',
189 r'Basic; realm="\"foo\\\\bar\""')
190 ]
191
192 for arg, expect in tests:
193 input = split_header_words([arg])
194 res = join_header_words(input)
195 self.assertEquals(res, expect, """
196When parsing: '%s'
197Expected: '%s'
198Got: '%s'
199Input was: '%s'
200""" % (arg, expect, res, input))
201
202
203class FakeResponse:
204 def __init__(self, headers=[], url=None):
205 """
206 headers: list of RFC822-style 'Key: value' strings
207 """
208 import mimetools, StringIO
209 f = StringIO.StringIO("\n".join(headers))
210 self._headers = mimetools.Message(f)
211 self._url = url
212 def info(self): return self._headers
213
214def interact_2965(cookiejar, url, *set_cookie_hdrs):
215 return _interact(cookiejar, url, set_cookie_hdrs, "Set-Cookie2")
216
217def interact_netscape(cookiejar, url, *set_cookie_hdrs):
218 return _interact(cookiejar, url, set_cookie_hdrs, "Set-Cookie")
219
220def _interact(cookiejar, url, set_cookie_hdrs, hdr_name):
221 """Perform a single request / response cycle, returning Cookie: header."""
222 from urllib2 import Request
223 req = Request(url)
224 cookiejar.add_cookie_header(req)
225 cookie_hdr = req.get_header("Cookie", "")
226 headers = []
227 for hdr in set_cookie_hdrs:
228 headers.append("%s: %s" % (hdr_name, hdr))
229 res = FakeResponse(headers, url)
230 cookiejar.extract_cookies(res, req)
231 return cookie_hdr
232
233
Martin v. Löwisc5574e82005-03-03 10:57:37 +0000234class FileCookieJarTests(TestCase):
235 def test_lwp_valueless_cookie(self):
236 # cookies with no value should be saved and loaded consistently
237 from cookielib import LWPCookieJar
238 filename = test_support.TESTFN
239 c = LWPCookieJar()
240 interact_netscape(c, "http://www.acme.com/", 'boo')
241 self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None)
242 try:
243 c.save(filename, ignore_discard=True)
244 c = LWPCookieJar()
245 c.load(filename, ignore_discard=True)
246 finally:
247 try: os.unlink(filename)
248 except OSError: pass
249 self.assertEqual(c._cookies["www.acme.com"]["/"]["boo"].value, None)
250
Neal Norwitz3e7de592005-12-23 21:24:35 +0000251 def test_bad_magic(self):
252 from cookielib import LWPCookieJar, MozillaCookieJar, LoadError
253 # IOErrors (eg. file doesn't exist) are allowed to propagate
254 filename = test_support.TESTFN
255 for cookiejar_class in LWPCookieJar, MozillaCookieJar:
256 c = cookiejar_class()
257 try:
258 c.load(filename="for this test to work, a file with this "
259 "filename should not exist")
260 except IOError, exc:
261 # exactly IOError, not LoadError
262 self.assertEqual(exc.__class__, IOError)
263 else:
264 self.fail("expected IOError for invalid filename")
265 # Invalid contents of cookies file (eg. bad magic string)
266 # causes a LoadError.
267 try:
268 f = open(filename, "w")
269 f.write("oops\n")
270 for cookiejar_class in LWPCookieJar, MozillaCookieJar:
271 c = cookiejar_class()
272 self.assertRaises(LoadError, c.load, filename)
273 finally:
274 try: os.unlink(filename)
275 except OSError: pass
Martin v. Löwisc5574e82005-03-03 10:57:37 +0000276
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000277class CookieTests(TestCase):
278 # XXX
279 # Get rid of string comparisons where not actually testing str / repr.
280 # .clear() etc.
281 # IP addresses like 50 (single number, no dot) and domain-matching
282 # functions (and is_HDN)? See draft RFC 2965 errata.
283 # Strictness switches
284 # is_third_party()
285 # unverifiability / third-party blocking
286 # Netscape cookies work the same as RFC 2965 with regard to port.
287 # Set-Cookie with negative max age.
288 # If turn RFC 2965 handling off, Set-Cookie2 cookies should not clobber
289 # Set-Cookie cookies.
290 # Cookie2 should be sent if *any* cookies are not V1 (ie. V0 OR V2 etc.).
291 # Cookies (V1 and V0) with no expiry date should be set to be discarded.
292 # RFC 2965 Quoting:
293 # Should accept unquoted cookie-attribute values? check errata draft.
294 # Which are required on the way in and out?
295 # Should always return quoted cookie-attribute values?
296 # Proper testing of when RFC 2965 clobbers Netscape (waiting for errata).
297 # Path-match on return (same for V0 and V1).
298 # RFC 2965 acceptance and returning rules
299 # Set-Cookie2 without version attribute is rejected.
300
301 # Netscape peculiarities list from Ronald Tschalar.
302 # The first two still need tests, the rest are covered.
303## - Quoting: only quotes around the expires value are recognized as such
304## (and yes, some folks quote the expires value); quotes around any other
305## value are treated as part of the value.
306## - White space: white space around names and values is ignored
307## - Default path: if no path parameter is given, the path defaults to the
308## path in the request-uri up to, but not including, the last '/'. Note
309## that this is entirely different from what the spec says.
310## - Commas and other delimiters: Netscape just parses until the next ';'.
311## This means it will allow commas etc inside values (and yes, both
312## commas and equals are commonly appear in the cookie value). This also
313## means that if you fold multiple Set-Cookie header fields into one,
314## comma-separated list, it'll be a headache to parse (at least my head
315## starts hurting everytime I think of that code).
316## - Expires: You'll get all sorts of date formats in the expires,
317## including emtpy expires attributes ("expires="). Be as flexible as you
318## can, and certainly don't expect the weekday to be there; if you can't
319## parse it, just ignore it and pretend it's a session cookie.
320## - Domain-matching: Netscape uses the 2-dot rule for _all_ domains, not
321## just the 7 special TLD's listed in their spec. And folks rely on
322## that...
323
324 def test_domain_return_ok(self):
325 # test optimization: .domain_return_ok() should filter out most
326 # domains in the CookieJar before we try to access them (because that
327 # may require disk access -- in particular, with MSIECookieJar)
328 # This is only a rough check for performance reasons, so it's not too
329 # critical as long as it's sufficiently liberal.
330 import cookielib, urllib2
331 pol = cookielib.DefaultCookiePolicy()
332 for url, domain, ok in [
333 ("http://foo.bar.com/", "blah.com", False),
334 ("http://foo.bar.com/", "rhubarb.blah.com", False),
335 ("http://foo.bar.com/", "rhubarb.foo.bar.com", False),
336 ("http://foo.bar.com/", ".foo.bar.com", True),
337 ("http://foo.bar.com/", "foo.bar.com", True),
338 ("http://foo.bar.com/", ".bar.com", True),
339 ("http://foo.bar.com/", "com", True),
340 ("http://foo.com/", "rhubarb.foo.com", False),
341 ("http://foo.com/", ".foo.com", True),
342 ("http://foo.com/", "foo.com", True),
343 ("http://foo.com/", "com", True),
344 ("http://foo/", "rhubarb.foo", False),
345 ("http://foo/", ".foo", True),
346 ("http://foo/", "foo", True),
347 ("http://foo/", "foo.local", True),
348 ("http://foo/", ".local", True),
349 ]:
350 request = urllib2.Request(url)
351 r = pol.domain_return_ok(domain, request)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000352 if ok: self.assertTrue(r)
353 else: self.assertTrue(not r)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000354
355 def test_missing_value(self):
356 from cookielib import MozillaCookieJar, lwp_cookie_str
357
358 # missing = sign in Cookie: header is regarded by Mozilla as a missing
359 # name, and by cookielib as a missing value
360 filename = test_support.TESTFN
361 c = MozillaCookieJar(filename)
362 interact_netscape(c, "http://www.acme.com/", 'eggs')
363 interact_netscape(c, "http://www.acme.com/", '"spam"; path=/foo/')
364 cookie = c._cookies["www.acme.com"]["/"]["eggs"]
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000365 self.assertTrue(cookie.value is None)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000366 self.assertEquals(cookie.name, "eggs")
367 cookie = c._cookies["www.acme.com"]['/foo/']['"spam"']
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000368 self.assertTrue(cookie.value is None)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000369 self.assertEquals(cookie.name, '"spam"')
370 self.assertEquals(lwp_cookie_str(cookie), (
371 r'"spam"; path="/foo/"; domain="www.acme.com"; '
372 'path_spec; discard; version=0'))
373 old_str = repr(c)
374 c.save(ignore_expires=True, ignore_discard=True)
375 try:
376 c = MozillaCookieJar(filename)
377 c.revert(ignore_expires=True, ignore_discard=True)
378 finally:
379 os.unlink(c.filename)
380 # cookies unchanged apart from lost info re. whether path was specified
381 self.assertEquals(
382 repr(c),
383 re.sub("path_specified=%s" % True, "path_specified=%s" % False,
384 old_str)
385 )
386 self.assertEquals(interact_netscape(c, "http://www.acme.com/foo/"),
387 '"spam"; eggs')
388
Neal Norwitz71dad722005-12-23 21:43:48 +0000389 def test_rfc2109_handling(self):
390 # RFC 2109 cookies are handled as RFC 2965 or Netscape cookies,
391 # dependent on policy settings
392 from cookielib import CookieJar, DefaultCookiePolicy
393
394 for rfc2109_as_netscape, rfc2965, version in [
395 # default according to rfc2965 if not explicitly specified
396 (None, False, 0),
397 (None, True, 1),
398 # explicit rfc2109_as_netscape
399 (False, False, None), # version None here means no cookie stored
400 (False, True, 1),
401 (True, False, 0),
402 (True, True, 0),
403 ]:
404 policy = DefaultCookiePolicy(
405 rfc2109_as_netscape=rfc2109_as_netscape,
406 rfc2965=rfc2965)
407 c = CookieJar(policy)
408 interact_netscape(c, "http://www.example.com/", "ni=ni; Version=1")
409 try:
410 cookie = c._cookies["www.example.com"]["/"]["ni"]
411 except KeyError:
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000412 self.assertTrue(version is None) # didn't expect a stored cookie
Neal Norwitz71dad722005-12-23 21:43:48 +0000413 else:
414 self.assertEqual(cookie.version, version)
415 # 2965 cookies are unaffected
416 interact_2965(c, "http://www.example.com/",
417 "foo=bar; Version=1")
418 if rfc2965:
419 cookie2965 = c._cookies["www.example.com"]["/"]["foo"]
420 self.assertEqual(cookie2965.version, 1)
421
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000422 def test_ns_parser(self):
423 from cookielib import CookieJar, DEFAULT_HTTP_PORT
424
425 c = CookieJar()
426 interact_netscape(c, "http://www.acme.com/",
427 'spam=eggs; DoMain=.acme.com; port; blArgh="feep"')
428 interact_netscape(c, "http://www.acme.com/", 'ni=ni; port=80,8080')
429 interact_netscape(c, "http://www.acme.com:80/", 'nini=ni')
430 interact_netscape(c, "http://www.acme.com:80/", 'foo=bar; expires=')
431 interact_netscape(c, "http://www.acme.com:80/", 'spam=eggs; '
432 'expires="Foo Bar 25 33:22:11 3022"')
433
434 cookie = c._cookies[".acme.com"]["/"]["spam"]
435 self.assertEquals(cookie.domain, ".acme.com")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000436 self.assertTrue(cookie.domain_specified)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000437 self.assertEquals(cookie.port, DEFAULT_HTTP_PORT)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000438 self.assertTrue(not cookie.port_specified)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000439 # case is preserved
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000440 self.assertTrue(cookie.has_nonstandard_attr("blArgh") and
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000441 not cookie.has_nonstandard_attr("blargh"))
442
443 cookie = c._cookies["www.acme.com"]["/"]["ni"]
444 self.assertEquals(cookie.domain, "www.acme.com")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000445 self.assertTrue(not cookie.domain_specified)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000446 self.assertEquals(cookie.port, "80,8080")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000447 self.assertTrue(cookie.port_specified)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000448
449 cookie = c._cookies["www.acme.com"]["/"]["nini"]
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000450 self.assertTrue(cookie.port is None)
451 self.assertTrue(not cookie.port_specified)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000452
453 # invalid expires should not cause cookie to be dropped
454 foo = c._cookies["www.acme.com"]["/"]["foo"]
455 spam = c._cookies["www.acme.com"]["/"]["foo"]
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000456 self.assertTrue(foo.expires is None)
457 self.assertTrue(spam.expires is None)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000458
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000459 def test_ns_parser_special_names(self):
460 # names such as 'expires' are not special in first name=value pair
461 # of Set-Cookie: header
462 from cookielib import CookieJar
463
464 c = CookieJar()
465 interact_netscape(c, "http://www.acme.com/", 'expires=eggs')
466 interact_netscape(c, "http://www.acme.com/", 'version=eggs; spam=eggs')
467
468 cookies = c._cookies["www.acme.com"]["/"]
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000469 self.assertTrue('expires' in cookies)
470 self.assertTrue('version' in cookies)
Martin v. Löwis4ea3ead2005-03-03 10:48:12 +0000471
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000472 def test_expires(self):
473 from cookielib import time2netscape, CookieJar
474
475 # if expires is in future, keep cookie...
476 c = CookieJar()
477 future = time2netscape(time.time()+3600)
478 interact_netscape(c, "http://www.acme.com/", 'spam="bar"; expires=%s' %
479 future)
480 self.assertEquals(len(c), 1)
481 now = time2netscape(time.time()-1)
482 # ... and if in past or present, discard it
483 interact_netscape(c, "http://www.acme.com/", 'foo="eggs"; expires=%s' %
484 now)
485 h = interact_netscape(c, "http://www.acme.com/")
486 self.assertEquals(len(c), 1)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000487 self.assertTrue('spam="bar"' in h and "foo" not in h)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000488
489 # max-age takes precedence over expires, and zero max-age is request to
490 # delete both new cookie and any old matching cookie
491 interact_netscape(c, "http://www.acme.com/", 'eggs="bar"; expires=%s' %
492 future)
493 interact_netscape(c, "http://www.acme.com/", 'bar="bar"; expires=%s' %
494 future)
495 self.assertEquals(len(c), 3)
496 interact_netscape(c, "http://www.acme.com/", 'eggs="bar"; '
497 'expires=%s; max-age=0' % future)
498 interact_netscape(c, "http://www.acme.com/", 'bar="bar"; '
499 'max-age=0; expires=%s' % future)
500 h = interact_netscape(c, "http://www.acme.com/")
501 self.assertEquals(len(c), 1)
502
503 # test expiry at end of session for cookies with no expires attribute
504 interact_netscape(c, "http://www.rhubarb.net/", 'whum="fizz"')
505 self.assertEquals(len(c), 2)
506 c.clear_session_cookies()
507 self.assertEquals(len(c), 1)
Ezio Melottiaa980582010-01-23 23:04:36 +0000508 self.assertIn('spam="bar"', h)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000509
510 # XXX RFC 2965 expiry rules (some apply to V0 too)
511
512 def test_default_path(self):
513 from cookielib import CookieJar, DefaultCookiePolicy
514
515 # RFC 2965
516 pol = DefaultCookiePolicy(rfc2965=True)
517
518 c = CookieJar(pol)
519 interact_2965(c, "http://www.acme.com/", 'spam="bar"; Version="1"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000520 self.assertIn("/", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000521
522 c = CookieJar(pol)
523 interact_2965(c, "http://www.acme.com/blah", 'eggs="bar"; Version="1"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000524 self.assertIn("/", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000525
526 c = CookieJar(pol)
527 interact_2965(c, "http://www.acme.com/blah/rhubarb",
528 'eggs="bar"; Version="1"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000529 self.assertIn("/blah/", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000530
531 c = CookieJar(pol)
532 interact_2965(c, "http://www.acme.com/blah/rhubarb/",
533 'eggs="bar"; Version="1"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000534 self.assertIn("/blah/rhubarb/", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000535
536 # Netscape
537
538 c = CookieJar()
539 interact_netscape(c, "http://www.acme.com/", 'spam="bar"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000540 self.assertIn("/", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000541
542 c = CookieJar()
543 interact_netscape(c, "http://www.acme.com/blah", 'eggs="bar"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000544 self.assertIn("/", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000545
546 c = CookieJar()
547 interact_netscape(c, "http://www.acme.com/blah/rhubarb", 'eggs="bar"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000548 self.assertIn("/blah", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000549
550 c = CookieJar()
551 interact_netscape(c, "http://www.acme.com/blah/rhubarb/", 'eggs="bar"')
Ezio Melottiaa980582010-01-23 23:04:36 +0000552 self.assertIn("/blah/rhubarb", c._cookies["www.acme.com"])
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000553
554 def test_escape_path(self):
555 from cookielib import escape_path
556 cases = [
557 # quoted safe
558 ("/foo%2f/bar", "/foo%2F/bar"),
559 ("/foo%2F/bar", "/foo%2F/bar"),
560 # quoted %
561 ("/foo%%/bar", "/foo%%/bar"),
562 # quoted unsafe
563 ("/fo%19o/bar", "/fo%19o/bar"),
564 ("/fo%7do/bar", "/fo%7Do/bar"),
565 # unquoted safe
566 ("/foo/bar&", "/foo/bar&"),
567 ("/foo//bar", "/foo//bar"),
568 ("\176/foo/bar", "\176/foo/bar"),
569 # unquoted unsafe
570 ("/foo\031/bar", "/foo%19/bar"),
571 ("/\175foo/bar", "/%7Dfoo/bar"),
572 # unicode
573 (u"/foo/bar\uabcd", "/foo/bar%EA%AF%8D"), # UTF-8 encoded
574 ]
575 for arg, result in cases:
576 self.assertEquals(escape_path(arg), result)
577
578 def test_request_path(self):
579 from urllib2 import Request
580 from cookielib import request_path
581 # with parameters
582 req = Request("http://www.example.com/rheum/rhaponicum;"
583 "foo=bar;sing=song?apples=pears&spam=eggs#ni")
584 self.assertEquals(request_path(req), "/rheum/rhaponicum;"
585 "foo=bar;sing=song?apples=pears&spam=eggs#ni")
586 # without parameters
587 req = Request("http://www.example.com/rheum/rhaponicum?"
588 "apples=pears&spam=eggs#ni")
589 self.assertEquals(request_path(req), "/rheum/rhaponicum?"
590 "apples=pears&spam=eggs#ni")
591 # missing final slash
592 req = Request("http://www.example.com")
593 self.assertEquals(request_path(req), "/")
594
595 def test_request_port(self):
596 from urllib2 import Request
597 from cookielib import request_port, DEFAULT_HTTP_PORT
598 req = Request("http://www.acme.com:1234/",
599 headers={"Host": "www.acme.com:4321"})
600 self.assertEquals(request_port(req), "1234")
601 req = Request("http://www.acme.com/",
602 headers={"Host": "www.acme.com:4321"})
603 self.assertEquals(request_port(req), DEFAULT_HTTP_PORT)
604
605 def test_request_host(self):
606 from urllib2 import Request
607 from cookielib import request_host
608 # this request is illegal (RFC2616, 14.2.3)
609 req = Request("http://1.1.1.1/",
610 headers={"Host": "www.acme.com:80"})
611 # libwww-perl wants this response, but that seems wrong (RFC 2616,
612 # section 5.2, point 1., and RFC 2965 section 1, paragraph 3)
613 #self.assertEquals(request_host(req), "www.acme.com")
614 self.assertEquals(request_host(req), "1.1.1.1")
615 req = Request("http://www.acme.com/",
616 headers={"Host": "irrelevant.com"})
617 self.assertEquals(request_host(req), "www.acme.com")
618 # not actually sure this one is valid Request object, so maybe should
619 # remove test for no host in url in request_host function?
620 req = Request("/resource.html",
621 headers={"Host": "www.acme.com"})
622 self.assertEquals(request_host(req), "www.acme.com")
623 # port shouldn't be in request-host
624 req = Request("http://www.acme.com:2345/resource.html",
625 headers={"Host": "www.acme.com:5432"})
626 self.assertEquals(request_host(req), "www.acme.com")
627
628 def test_is_HDN(self):
629 from cookielib import is_HDN
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000630 self.assertTrue(is_HDN("foo.bar.com"))
631 self.assertTrue(is_HDN("1foo2.3bar4.5com"))
632 self.assertTrue(not is_HDN("192.168.1.1"))
633 self.assertTrue(not is_HDN(""))
634 self.assertTrue(not is_HDN("."))
635 self.assertTrue(not is_HDN(".foo.bar.com"))
636 self.assertTrue(not is_HDN("..foo"))
637 self.assertTrue(not is_HDN("foo."))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000638
639 def test_reach(self):
640 from cookielib import reach
641 self.assertEquals(reach("www.acme.com"), ".acme.com")
642 self.assertEquals(reach("acme.com"), "acme.com")
643 self.assertEquals(reach("acme.local"), ".local")
644 self.assertEquals(reach(".local"), ".local")
645 self.assertEquals(reach(".com"), ".com")
646 self.assertEquals(reach("."), ".")
647 self.assertEquals(reach(""), "")
648 self.assertEquals(reach("192.168.0.1"), "192.168.0.1")
649
650 def test_domain_match(self):
651 from cookielib import domain_match, user_domain_match
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000652 self.assertTrue(domain_match("192.168.1.1", "192.168.1.1"))
653 self.assertTrue(not domain_match("192.168.1.1", ".168.1.1"))
654 self.assertTrue(domain_match("x.y.com", "x.Y.com"))
655 self.assertTrue(domain_match("x.y.com", ".Y.com"))
656 self.assertTrue(not domain_match("x.y.com", "Y.com"))
657 self.assertTrue(domain_match("a.b.c.com", ".c.com"))
658 self.assertTrue(not domain_match(".c.com", "a.b.c.com"))
659 self.assertTrue(domain_match("example.local", ".local"))
660 self.assertTrue(not domain_match("blah.blah", ""))
661 self.assertTrue(not domain_match("", ".rhubarb.rhubarb"))
662 self.assertTrue(domain_match("", ""))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000663
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000664 self.assertTrue(user_domain_match("acme.com", "acme.com"))
665 self.assertTrue(not user_domain_match("acme.com", ".acme.com"))
666 self.assertTrue(user_domain_match("rhubarb.acme.com", ".acme.com"))
667 self.assertTrue(user_domain_match("www.rhubarb.acme.com", ".acme.com"))
668 self.assertTrue(user_domain_match("x.y.com", "x.Y.com"))
669 self.assertTrue(user_domain_match("x.y.com", ".Y.com"))
670 self.assertTrue(not user_domain_match("x.y.com", "Y.com"))
671 self.assertTrue(user_domain_match("y.com", "Y.com"))
672 self.assertTrue(not user_domain_match(".y.com", "Y.com"))
673 self.assertTrue(user_domain_match(".y.com", ".Y.com"))
674 self.assertTrue(user_domain_match("x.y.com", ".com"))
675 self.assertTrue(not user_domain_match("x.y.com", "com"))
676 self.assertTrue(not user_domain_match("x.y.com", "m"))
677 self.assertTrue(not user_domain_match("x.y.com", ".m"))
678 self.assertTrue(not user_domain_match("x.y.com", ""))
679 self.assertTrue(not user_domain_match("x.y.com", "."))
680 self.assertTrue(user_domain_match("192.168.1.1", "192.168.1.1"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000681 # not both HDNs, so must string-compare equal to match
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000682 self.assertTrue(not user_domain_match("192.168.1.1", ".168.1.1"))
683 self.assertTrue(not user_domain_match("192.168.1.1", "."))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000684 # empty string is a special case
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000685 self.assertTrue(not user_domain_match("192.168.1.1", ""))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000686
687 def test_wrong_domain(self):
688 # Cookies whose effective request-host name does not domain-match the
689 # domain are rejected.
690
691 # XXX far from complete
692 from cookielib import CookieJar
693 c = CookieJar()
694 interact_2965(c, "http://www.nasty.com/",
695 'foo=bar; domain=friendly.org; Version="1"')
696 self.assertEquals(len(c), 0)
697
Georg Brandla166a912006-05-08 17:28:47 +0000698 def test_strict_domain(self):
699 # Cookies whose domain is a country-code tld like .co.uk should
700 # not be set if CookiePolicy.strict_domain is true.
701 from cookielib import CookieJar, DefaultCookiePolicy
702
703 cp = DefaultCookiePolicy(strict_domain=True)
704 cj = CookieJar(policy=cp)
705 interact_netscape(cj, "http://example.co.uk/", 'no=problemo')
706 interact_netscape(cj, "http://example.co.uk/",
707 'okey=dokey; Domain=.example.co.uk')
708 self.assertEquals(len(cj), 2)
709 for pseudo_tld in [".co.uk", ".org.za", ".tx.us", ".name.us"]:
710 interact_netscape(cj, "http://example.%s/" % pseudo_tld,
711 'spam=eggs; Domain=.co.uk')
712 self.assertEquals(len(cj), 2)
713
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000714 def test_two_component_domain_ns(self):
715 # Netscape: .www.bar.com, www.bar.com, .bar.com, bar.com, no domain
716 # should all get accepted, as should .acme.com, acme.com and no domain
717 # for 2-component domains like acme.com.
718 from cookielib import CookieJar, DefaultCookiePolicy
719
720 c = CookieJar()
721
722 # two-component V0 domain is OK
723 interact_netscape(c, "http://foo.net/", 'ns=bar')
724 self.assertEquals(len(c), 1)
725 self.assertEquals(c._cookies["foo.net"]["/"]["ns"].value, "bar")
726 self.assertEquals(interact_netscape(c, "http://foo.net/"), "ns=bar")
727 # *will* be returned to any other domain (unlike RFC 2965)...
728 self.assertEquals(interact_netscape(c, "http://www.foo.net/"),
729 "ns=bar")
730 # ...unless requested otherwise
731 pol = DefaultCookiePolicy(
732 strict_ns_domain=DefaultCookiePolicy.DomainStrictNonDomain)
733 c.set_policy(pol)
734 self.assertEquals(interact_netscape(c, "http://www.foo.net/"), "")
735
736 # unlike RFC 2965, even explicit two-component domain is OK,
737 # because .foo.net matches foo.net
738 interact_netscape(c, "http://foo.net/foo/",
739 'spam1=eggs; domain=foo.net')
740 # even if starts with a dot -- in NS rules, .foo.net matches foo.net!
741 interact_netscape(c, "http://foo.net/foo/bar/",
742 'spam2=eggs; domain=.foo.net')
743 self.assertEquals(len(c), 3)
744 self.assertEquals(c._cookies[".foo.net"]["/foo"]["spam1"].value,
745 "eggs")
746 self.assertEquals(c._cookies[".foo.net"]["/foo/bar"]["spam2"].value,
747 "eggs")
748 self.assertEquals(interact_netscape(c, "http://foo.net/foo/bar/"),
749 "spam2=eggs; spam1=eggs; ns=bar")
750
751 # top-level domain is too general
752 interact_netscape(c, "http://foo.net/", 'nini="ni"; domain=.net')
753 self.assertEquals(len(c), 3)
754
755## # Netscape protocol doesn't allow non-special top level domains (such
756## # as co.uk) in the domain attribute unless there are at least three
757## # dots in it.
758 # Oh yes it does! Real implementations don't check this, and real
759 # cookies (of course) rely on that behaviour.
760 interact_netscape(c, "http://foo.co.uk", 'nasty=trick; domain=.co.uk')
761## self.assertEquals(len(c), 2)
762 self.assertEquals(len(c), 4)
763
764 def test_two_component_domain_rfc2965(self):
765 from cookielib import CookieJar, DefaultCookiePolicy
766
767 pol = DefaultCookiePolicy(rfc2965=True)
768 c = CookieJar(pol)
769
770 # two-component V1 domain is OK
771 interact_2965(c, "http://foo.net/", 'foo=bar; Version="1"')
772 self.assertEquals(len(c), 1)
773 self.assertEquals(c._cookies["foo.net"]["/"]["foo"].value, "bar")
774 self.assertEquals(interact_2965(c, "http://foo.net/"),
775 "$Version=1; foo=bar")
776 # won't be returned to any other domain (because domain was implied)
777 self.assertEquals(interact_2965(c, "http://www.foo.net/"), "")
778
779 # unless domain is given explicitly, because then it must be
780 # rewritten to start with a dot: foo.net --> .foo.net, which does
781 # not domain-match foo.net
782 interact_2965(c, "http://foo.net/foo",
783 'spam=eggs; domain=foo.net; path=/foo; Version="1"')
784 self.assertEquals(len(c), 1)
785 self.assertEquals(interact_2965(c, "http://foo.net/foo"),
786 "$Version=1; foo=bar")
787
788 # explicit foo.net from three-component domain www.foo.net *does* get
789 # set, because .foo.net domain-matches .foo.net
790 interact_2965(c, "http://www.foo.net/foo/",
791 'spam=eggs; domain=foo.net; Version="1"')
792 self.assertEquals(c._cookies[".foo.net"]["/foo/"]["spam"].value,
793 "eggs")
794 self.assertEquals(len(c), 2)
795 self.assertEquals(interact_2965(c, "http://foo.net/foo/"),
796 "$Version=1; foo=bar")
797 self.assertEquals(interact_2965(c, "http://www.foo.net/foo/"),
798 '$Version=1; spam=eggs; $Domain="foo.net"')
799
800 # top-level domain is too general
801 interact_2965(c, "http://foo.net/",
802 'ni="ni"; domain=".net"; Version="1"')
803 self.assertEquals(len(c), 2)
804
805 # RFC 2965 doesn't require blocking this
806 interact_2965(c, "http://foo.co.uk/",
807 'nasty=trick; domain=.co.uk; Version="1"')
808 self.assertEquals(len(c), 3)
809
810 def test_domain_allow(self):
811 from cookielib import CookieJar, DefaultCookiePolicy
812 from urllib2 import Request
813
814 c = CookieJar(policy=DefaultCookiePolicy(
815 blocked_domains=["acme.com"],
816 allowed_domains=["www.acme.com"]))
817
818 req = Request("http://acme.com/")
819 headers = ["Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/"]
820 res = FakeResponse(headers, "http://acme.com/")
821 c.extract_cookies(res, req)
822 self.assertEquals(len(c), 0)
823
824 req = Request("http://www.acme.com/")
825 res = FakeResponse(headers, "http://www.acme.com/")
826 c.extract_cookies(res, req)
827 self.assertEquals(len(c), 1)
828
829 req = Request("http://www.coyote.com/")
830 res = FakeResponse(headers, "http://www.coyote.com/")
831 c.extract_cookies(res, req)
832 self.assertEquals(len(c), 1)
833
834 # set a cookie with non-allowed domain...
835 req = Request("http://www.coyote.com/")
836 res = FakeResponse(headers, "http://www.coyote.com/")
837 cookies = c.make_cookies(res, req)
838 c.set_cookie(cookies[0])
839 self.assertEquals(len(c), 2)
840 # ... and check is doesn't get returned
841 c.add_cookie_header(req)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000842 self.assertTrue(not req.has_header("Cookie"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000843
844 def test_domain_block(self):
845 from cookielib import CookieJar, DefaultCookiePolicy
846 from urllib2 import Request
847
848 pol = DefaultCookiePolicy(
849 rfc2965=True, blocked_domains=[".acme.com"])
850 c = CookieJar(policy=pol)
851 headers = ["Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/"]
852
853 req = Request("http://www.acme.com/")
854 res = FakeResponse(headers, "http://www.acme.com/")
855 c.extract_cookies(res, req)
856 self.assertEquals(len(c), 0)
857
858 p = pol.set_blocked_domains(["acme.com"])
859 c.extract_cookies(res, req)
860 self.assertEquals(len(c), 1)
861
862 c.clear()
863 req = Request("http://www.roadrunner.net/")
864 res = FakeResponse(headers, "http://www.roadrunner.net/")
865 c.extract_cookies(res, req)
866 self.assertEquals(len(c), 1)
867 req = Request("http://www.roadrunner.net/")
868 c.add_cookie_header(req)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000869 self.assertTrue((req.has_header("Cookie") and
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000870 req.has_header("Cookie2")))
871
872 c.clear()
873 pol.set_blocked_domains([".acme.com"])
874 c.extract_cookies(res, req)
875 self.assertEquals(len(c), 1)
876
877 # set a cookie with blocked domain...
878 req = Request("http://www.acme.com/")
879 res = FakeResponse(headers, "http://www.acme.com/")
880 cookies = c.make_cookies(res, req)
881 c.set_cookie(cookies[0])
882 self.assertEquals(len(c), 2)
883 # ... and check is doesn't get returned
884 c.add_cookie_header(req)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000885 self.assertTrue(not req.has_header("Cookie"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000886
887 def test_secure(self):
888 from cookielib import CookieJar, DefaultCookiePolicy
889
890 for ns in True, False:
891 for whitespace in " ", "":
892 c = CookieJar()
893 if ns:
894 pol = DefaultCookiePolicy(rfc2965=False)
895 int = interact_netscape
896 vs = ""
897 else:
898 pol = DefaultCookiePolicy(rfc2965=True)
899 int = interact_2965
900 vs = "; Version=1"
901 c.set_policy(pol)
902 url = "http://www.acme.com/"
903 int(c, url, "foo1=bar%s%s" % (vs, whitespace))
904 int(c, url, "foo2=bar%s; secure%s" % (vs, whitespace))
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000905 self.assertTrue(
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000906 not c._cookies["www.acme.com"]["/"]["foo1"].secure,
907 "non-secure cookie registered secure")
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000908 self.assertTrue(
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000909 c._cookies["www.acme.com"]["/"]["foo2"].secure,
910 "secure cookie registered non-secure")
911
912 def test_quote_cookie_value(self):
913 from cookielib import CookieJar, DefaultCookiePolicy
914 c = CookieJar(policy=DefaultCookiePolicy(rfc2965=True))
915 interact_2965(c, "http://www.acme.com/", r'foo=\b"a"r; Version=1')
916 h = interact_2965(c, "http://www.acme.com/")
917 self.assertEquals(h, r'$Version=1; foo=\\b\"a\"r')
918
919 def test_missing_final_slash(self):
920 # Missing slash from request URL's abs_path should be assumed present.
921 from cookielib import CookieJar, DefaultCookiePolicy
922 from urllib2 import Request
923 url = "http://www.acme.com"
924 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
925 interact_2965(c, url, "foo=bar; Version=1")
926 req = Request(url)
927 self.assertEquals(len(c), 1)
928 c.add_cookie_header(req)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000929 self.assertTrue(req.has_header("Cookie"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000930
931 def test_domain_mirror(self):
932 from cookielib import CookieJar, DefaultCookiePolicy
933
934 pol = DefaultCookiePolicy(rfc2965=True)
935
936 c = CookieJar(pol)
937 url = "http://foo.bar.com/"
938 interact_2965(c, url, "spam=eggs; Version=1")
939 h = interact_2965(c, url)
Ezio Melottiaa980582010-01-23 23:04:36 +0000940 self.assertNotIn("Domain", h,
941 "absent domain returned with domain present")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000942
943 c = CookieJar(pol)
944 url = "http://foo.bar.com/"
945 interact_2965(c, url, 'spam=eggs; Version=1; Domain=.bar.com')
946 h = interact_2965(c, url)
Ezio Melottiaa980582010-01-23 23:04:36 +0000947 self.assertIn('$Domain=".bar.com"', h, "domain not returned")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000948
949 c = CookieJar(pol)
950 url = "http://foo.bar.com/"
951 # note missing initial dot in Domain
952 interact_2965(c, url, 'spam=eggs; Version=1; Domain=bar.com')
953 h = interact_2965(c, url)
Ezio Melottiaa980582010-01-23 23:04:36 +0000954 self.assertIn('$Domain="bar.com"', h, "domain not returned")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000955
956 def test_path_mirror(self):
957 from cookielib import CookieJar, DefaultCookiePolicy
958
959 pol = DefaultCookiePolicy(rfc2965=True)
960
961 c = CookieJar(pol)
962 url = "http://foo.bar.com/"
963 interact_2965(c, url, "spam=eggs; Version=1")
964 h = interact_2965(c, url)
Ezio Melottiaa980582010-01-23 23:04:36 +0000965 self.assertNotIn("Path", h, "absent path returned with path present")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000966
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)
Ezio Melottiaa980582010-01-23 23:04:36 +0000971 self.assertIn('$Path="/"', h, "path not returned")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000972
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)
Ezio Melottiaa980582010-01-23 23:04:36 +0000982 self.assertNotIn("Port", h, "absent port returned with port present")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000983
984 c = CookieJar(pol)
985 url = "http://foo.bar.com/"
986 interact_2965(c, url, "spam=eggs; Version=1; Port")
987 h = interact_2965(c, url)
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000988 self.assertTrue(re.search("\$Port([^=]|$)", h),
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000989 "port with no value not returned with no value")
990
991 c = CookieJar(pol)
992 url = "http://foo.bar.com/"
993 interact_2965(c, url, 'spam=eggs; Version=1; Port="80"')
994 h = interact_2965(c, url)
Ezio Melottiaa980582010-01-23 23:04:36 +0000995 self.assertIn('$Port="80"', h,
996 "port with single value not returned with single value")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +0000997
998 c = CookieJar(pol)
999 url = "http://foo.bar.com/"
1000 interact_2965(c, url, 'spam=eggs; Version=1; Port="80,8080"')
1001 h = interact_2965(c, url)
Ezio Melottiaa980582010-01-23 23:04:36 +00001002 self.assertIn('$Port="80,8080"', h,
1003 "port with multiple values not returned with multiple "
1004 "values")
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001005
1006 def test_no_return_comment(self):
1007 from cookielib import CookieJar, DefaultCookiePolicy
1008
1009 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1010 url = "http://foo.bar.com/"
1011 interact_2965(c, url, 'spam=eggs; Version=1; '
1012 'Comment="does anybody read these?"; '
1013 'CommentURL="http://foo.bar.net/comment.html"')
1014 h = interact_2965(c, url)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001015 self.assertTrue(
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001016 "Comment" not in h,
1017 "Comment or CommentURL cookie-attributes returned to server")
1018
1019 def test_Cookie_iterator(self):
1020 from cookielib import CookieJar, Cookie, DefaultCookiePolicy
1021
1022 cs = CookieJar(DefaultCookiePolicy(rfc2965=True))
1023 # add some random cookies
1024 interact_2965(cs, "http://blah.spam.org/", 'foo=eggs; Version=1; '
1025 'Comment="does anybody read these?"; '
1026 'CommentURL="http://foo.bar.net/comment.html"')
1027 interact_netscape(cs, "http://www.acme.com/blah/", "spam=bar; secure")
1028 interact_2965(cs, "http://www.acme.com/blah/",
1029 "foo=bar; secure; Version=1")
1030 interact_2965(cs, "http://www.acme.com/blah/",
1031 "foo=bar; path=/; Version=1")
1032 interact_2965(cs, "http://www.sol.no",
1033 r'bang=wallop; version=1; domain=".sol.no"; '
1034 r'port="90,100, 80,8080"; '
1035 r'max-age=100; Comment = "Just kidding! (\"|\\\\) "')
1036
1037 versions = [1, 1, 1, 0, 1]
1038 names = ["bang", "foo", "foo", "spam", "foo"]
1039 domains = [".sol.no", "blah.spam.org", "www.acme.com",
1040 "www.acme.com", "www.acme.com"]
1041 paths = ["/", "/", "/", "/blah", "/blah/"]
1042
1043 for i in range(4):
1044 i = 0
1045 for c in cs:
Ezio Melottib0f5adc2010-01-24 16:58:36 +00001046 self.assertIsInstance(c, Cookie)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001047 self.assertEquals(c.version, versions[i])
1048 self.assertEquals(c.name, names[i])
1049 self.assertEquals(c.domain, domains[i])
1050 self.assertEquals(c.path, paths[i])
1051 i = i + 1
1052
1053 def test_parse_ns_headers(self):
1054 from cookielib import parse_ns_headers
1055
1056 # missing domain value (invalid cookie)
1057 self.assertEquals(
1058 parse_ns_headers(["foo=bar; path=/; domain"]),
1059 [[("foo", "bar"),
1060 ("path", "/"), ("domain", None), ("version", "0")]]
1061 )
1062 # invalid expires value
1063 self.assertEquals(
1064 parse_ns_headers(["foo=bar; expires=Foo Bar 12 33:22:11 2000"]),
1065 [[("foo", "bar"), ("expires", None), ("version", "0")]]
1066 )
1067 # missing cookie value (valid cookie)
1068 self.assertEquals(
1069 parse_ns_headers(["foo"]),
1070 [[("foo", None), ("version", "0")]]
1071 )
1072 # shouldn't add version if header is empty
1073 self.assertEquals(parse_ns_headers([""]), [])
1074
1075 def test_bad_cookie_header(self):
1076
1077 def cookiejar_from_cookie_headers(headers):
1078 from cookielib import CookieJar
1079 from urllib2 import Request
1080 c = CookieJar()
1081 req = Request("http://www.example.com/")
1082 r = FakeResponse(headers, "http://www.example.com/")
1083 c.extract_cookies(r, req)
1084 return c
1085
1086 # none of these bad headers should cause an exception to be raised
1087 for headers in [
1088 ["Set-Cookie: "], # actually, nothing wrong with this
1089 ["Set-Cookie2: "], # ditto
1090 # missing domain value
1091 ["Set-Cookie2: a=foo; path=/; Version=1; domain"],
1092 # bad max-age
1093 ["Set-Cookie: b=foo; max-age=oops"],
1094 ]:
1095 c = cookiejar_from_cookie_headers(headers)
1096 # these bad cookies shouldn't be set
1097 self.assertEquals(len(c), 0)
1098
1099 # cookie with invalid expires is treated as session cookie
1100 headers = ["Set-Cookie: c=foo; expires=Foo Bar 12 33:22:11 2000"]
1101 c = cookiejar_from_cookie_headers(headers)
1102 cookie = c._cookies["www.example.com"]["/"]["c"]
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001103 self.assertTrue(cookie.expires is None)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001104
1105
1106class LWPCookieTests(TestCase):
1107 # Tests taken from libwww-perl, with a few modifications and additions.
1108
1109 def test_netscape_example_1(self):
1110 from cookielib import CookieJar, DefaultCookiePolicy
1111 from urllib2 import Request
1112
1113 #-------------------------------------------------------------------
1114 # First we check that it works for the original example at
1115 # http://www.netscape.com/newsref/std/cookie_spec.html
1116
1117 # Client requests a document, and receives in the response:
1118 #
1119 # Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT
1120 #
1121 # When client requests a URL in path "/" on this server, it sends:
1122 #
1123 # Cookie: CUSTOMER=WILE_E_COYOTE
1124 #
1125 # Client requests a document, and receives in the response:
1126 #
1127 # Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
1128 #
1129 # When client requests a URL in path "/" on this server, it sends:
1130 #
1131 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
1132 #
1133 # Client receives:
1134 #
1135 # Set-Cookie: SHIPPING=FEDEX; path=/fo
1136 #
1137 # When client requests a URL in path "/" on this server, it sends:
1138 #
1139 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
1140 #
1141 # When client requests a URL in path "/foo" on this server, it sends:
1142 #
1143 # Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX
1144 #
1145 # The last Cookie is buggy, because both specifications say that the
1146 # most specific cookie must be sent first. SHIPPING=FEDEX is the
1147 # most specific and should thus be first.
1148
1149 year_plus_one = time.localtime()[0] + 1
1150
1151 headers = []
1152
1153 c = CookieJar(DefaultCookiePolicy(rfc2965 = True))
1154
1155 #req = Request("http://1.1.1.1/",
1156 # headers={"Host": "www.acme.com:80"})
1157 req = Request("http://www.acme.com:80/",
1158 headers={"Host": "www.acme.com:80"})
1159
1160 headers.append(
1161 "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/ ; "
1162 "expires=Wednesday, 09-Nov-%d 23:12:40 GMT" % year_plus_one)
1163 res = FakeResponse(headers, "http://www.acme.com/")
1164 c.extract_cookies(res, req)
1165
1166 req = Request("http://www.acme.com/")
1167 c.add_cookie_header(req)
1168
1169 self.assertEqual(req.get_header("Cookie"), "CUSTOMER=WILE_E_COYOTE")
1170 self.assertEqual(req.get_header("Cookie2"), '$Version="1"')
1171
1172 headers.append("Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/")
1173 res = FakeResponse(headers, "http://www.acme.com/")
1174 c.extract_cookies(res, req)
1175
1176 req = Request("http://www.acme.com/foo/bar")
1177 c.add_cookie_header(req)
1178
1179 h = req.get_header("Cookie")
Ezio Melottiaa980582010-01-23 23:04:36 +00001180 self.assertIn("PART_NUMBER=ROCKET_LAUNCHER_0001", h)
1181 self.assertIn("CUSTOMER=WILE_E_COYOTE", h)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001182
1183 headers.append('Set-Cookie: SHIPPING=FEDEX; path=/foo')
1184 res = FakeResponse(headers, "http://www.acme.com")
1185 c.extract_cookies(res, req)
1186
1187 req = Request("http://www.acme.com/")
1188 c.add_cookie_header(req)
1189
1190 h = req.get_header("Cookie")
Ezio Melottiaa980582010-01-23 23:04:36 +00001191 self.assertIn("PART_NUMBER=ROCKET_LAUNCHER_0001", h)
1192 self.assertIn("CUSTOMER=WILE_E_COYOTE", h)
1193 self.assertNotIn("SHIPPING=FEDEX", h)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001194
1195 req = Request("http://www.acme.com/foo/")
1196 c.add_cookie_header(req)
1197
1198 h = req.get_header("Cookie")
Ezio Melottiaa980582010-01-23 23:04:36 +00001199 self.assertIn("PART_NUMBER=ROCKET_LAUNCHER_0001", h)
1200 self.assertIn("CUSTOMER=WILE_E_COYOTE", h)
1201 self.assertTrue(h.startswith("SHIPPING=FEDEX;"))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001202
1203 def test_netscape_example_2(self):
1204 from cookielib import CookieJar
1205 from urllib2 import Request
1206
1207 # Second Example transaction sequence:
1208 #
1209 # Assume all mappings from above have been cleared.
1210 #
1211 # Client receives:
1212 #
1213 # Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
1214 #
1215 # When client requests a URL in path "/" on this server, it sends:
1216 #
1217 # Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001
1218 #
1219 # Client receives:
1220 #
1221 # Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo
1222 #
1223 # When client requests a URL in path "/ammo" on this server, it sends:
1224 #
1225 # Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001
1226 #
1227 # NOTE: There are two name/value pairs named "PART_NUMBER" due to
1228 # the inheritance of the "/" mapping in addition to the "/ammo" mapping.
1229
1230 c = CookieJar()
1231 headers = []
1232
1233 req = Request("http://www.acme.com/")
1234 headers.append("Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/")
1235 res = FakeResponse(headers, "http://www.acme.com/")
1236
1237 c.extract_cookies(res, req)
1238
1239 req = Request("http://www.acme.com/")
1240 c.add_cookie_header(req)
1241
1242 self.assertEquals(req.get_header("Cookie"),
1243 "PART_NUMBER=ROCKET_LAUNCHER_0001")
1244
1245 headers.append(
1246 "Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo")
1247 res = FakeResponse(headers, "http://www.acme.com/")
1248 c.extract_cookies(res, req)
1249
1250 req = Request("http://www.acme.com/ammo")
1251 c.add_cookie_header(req)
1252
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001253 self.assertTrue(re.search(r"PART_NUMBER=RIDING_ROCKET_0023;\s*"
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001254 "PART_NUMBER=ROCKET_LAUNCHER_0001",
1255 req.get_header("Cookie")))
1256
1257 def test_ietf_example_1(self):
1258 from cookielib import CookieJar, DefaultCookiePolicy
1259 #-------------------------------------------------------------------
1260 # Then we test with the examples from draft-ietf-http-state-man-mec-03.txt
1261 #
1262 # 5. EXAMPLES
1263
1264 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1265
1266 #
1267 # 5.1 Example 1
1268 #
1269 # Most detail of request and response headers has been omitted. Assume
1270 # the user agent has no stored cookies.
1271 #
1272 # 1. User Agent -> Server
1273 #
1274 # POST /acme/login HTTP/1.1
1275 # [form data]
1276 #
1277 # User identifies self via a form.
1278 #
1279 # 2. Server -> User Agent
1280 #
1281 # HTTP/1.1 200 OK
1282 # Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
1283 #
1284 # Cookie reflects user's identity.
1285
1286 cookie = interact_2965(
1287 c, 'http://www.acme.com/acme/login',
1288 'Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"')
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001289 self.assertTrue(not cookie)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001290
1291 #
1292 # 3. User Agent -> Server
1293 #
1294 # POST /acme/pickitem HTTP/1.1
1295 # Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
1296 # [form data]
1297 #
1298 # User selects an item for ``shopping basket.''
1299 #
1300 # 4. Server -> User Agent
1301 #
1302 # HTTP/1.1 200 OK
1303 # Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
1304 # Path="/acme"
1305 #
1306 # Shopping basket contains an item.
1307
1308 cookie = interact_2965(c, 'http://www.acme.com/acme/pickitem',
1309 'Part_Number="Rocket_Launcher_0001"; '
1310 'Version="1"; Path="/acme"');
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001311 self.assertTrue(re.search(
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001312 r'^\$Version="?1"?; Customer="?WILE_E_COYOTE"?; \$Path="/acme"$',
1313 cookie))
1314
1315 #
1316 # 5. User Agent -> Server
1317 #
1318 # POST /acme/shipping HTTP/1.1
1319 # Cookie: $Version="1";
1320 # Customer="WILE_E_COYOTE"; $Path="/acme";
1321 # Part_Number="Rocket_Launcher_0001"; $Path="/acme"
1322 # [form data]
1323 #
1324 # User selects shipping method from form.
1325 #
1326 # 6. Server -> User Agent
1327 #
1328 # HTTP/1.1 200 OK
1329 # Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"
1330 #
1331 # New cookie reflects shipping method.
1332
1333 cookie = interact_2965(c, "http://www.acme.com/acme/shipping",
1334 'Shipping="FedEx"; Version="1"; Path="/acme"')
1335
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001336 self.assertTrue(re.search(r'^\$Version="?1"?;', cookie))
1337 self.assertTrue(re.search(r'Part_Number="?Rocket_Launcher_0001"?;'
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001338 '\s*\$Path="\/acme"', cookie))
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001339 self.assertTrue(re.search(r'Customer="?WILE_E_COYOTE"?;\s*\$Path="\/acme"',
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001340 cookie))
1341
1342 #
1343 # 7. User Agent -> Server
1344 #
1345 # POST /acme/process HTTP/1.1
1346 # Cookie: $Version="1";
1347 # Customer="WILE_E_COYOTE"; $Path="/acme";
1348 # Part_Number="Rocket_Launcher_0001"; $Path="/acme";
1349 # Shipping="FedEx"; $Path="/acme"
1350 # [form data]
1351 #
1352 # User chooses to process order.
1353 #
1354 # 8. Server -> User Agent
1355 #
1356 # HTTP/1.1 200 OK
1357 #
1358 # Transaction is complete.
1359
1360 cookie = interact_2965(c, "http://www.acme.com/acme/process")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001361 self.assertTrue(
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001362 re.search(r'Shipping="?FedEx"?;\s*\$Path="\/acme"', cookie) and
1363 "WILE_E_COYOTE" in cookie)
1364
1365 #
1366 # The user agent makes a series of requests on the origin server, after
1367 # each of which it receives a new cookie. All the cookies have the same
1368 # Path attribute and (default) domain. Because the request URLs all have
1369 # /acme as a prefix, and that matches the Path attribute, each request
1370 # contains all the cookies received so far.
1371
1372 def test_ietf_example_2(self):
1373 from cookielib import CookieJar, DefaultCookiePolicy
1374
1375 # 5.2 Example 2
1376 #
1377 # This example illustrates the effect of the Path attribute. All detail
1378 # of request and response headers has been omitted. Assume the user agent
1379 # has no stored cookies.
1380
1381 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1382
1383 # Imagine the user agent has received, in response to earlier requests,
1384 # the response headers
1385 #
1386 # Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
1387 # Path="/acme"
1388 #
1389 # and
1390 #
1391 # Set-Cookie2: Part_Number="Riding_Rocket_0023"; Version="1";
1392 # Path="/acme/ammo"
1393
1394 interact_2965(
1395 c, "http://www.acme.com/acme/ammo/specific",
1396 'Part_Number="Rocket_Launcher_0001"; Version="1"; Path="/acme"',
1397 'Part_Number="Riding_Rocket_0023"; Version="1"; Path="/acme/ammo"')
1398
1399 # A subsequent request by the user agent to the (same) server for URLs of
1400 # the form /acme/ammo/... would include the following request header:
1401 #
1402 # Cookie: $Version="1";
1403 # Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
1404 # Part_Number="Rocket_Launcher_0001"; $Path="/acme"
1405 #
1406 # Note that the NAME=VALUE pair for the cookie with the more specific Path
1407 # attribute, /acme/ammo, comes before the one with the less specific Path
1408 # attribute, /acme. Further note that the same cookie name appears more
1409 # than once.
1410
1411 cookie = interact_2965(c, "http://www.acme.com/acme/ammo/...")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001412 self.assertTrue(
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001413 re.search(r"Riding_Rocket_0023.*Rocket_Launcher_0001", cookie))
1414
1415 # A subsequent request by the user agent to the (same) server for a URL of
1416 # the form /acme/parts/ would include the following request header:
1417 #
1418 # Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"
1419 #
1420 # Here, the second cookie's Path attribute /acme/ammo is not a prefix of
1421 # the request URL, /acme/parts/, so the cookie does not get forwarded to
1422 # the server.
1423
1424 cookie = interact_2965(c, "http://www.acme.com/acme/parts/")
Ezio Melottiaa980582010-01-23 23:04:36 +00001425 self.assertIn("Rocket_Launcher_0001", cookie)
1426 self.assertNotIn("Riding_Rocket_0023", cookie)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001427
1428 def test_rejection(self):
1429 # Test rejection of Set-Cookie2 responses based on domain, path, port.
1430 from cookielib import DefaultCookiePolicy, LWPCookieJar
1431
1432 pol = DefaultCookiePolicy(rfc2965=True)
1433
1434 c = LWPCookieJar(policy=pol)
1435
1436 max_age = "max-age=3600"
1437
1438 # illegal domain (no embedded dots)
1439 cookie = interact_2965(c, "http://www.acme.com",
1440 'foo=bar; domain=".com"; version=1')
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001441 self.assertTrue(not c)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001442
1443 # legal domain
1444 cookie = interact_2965(c, "http://www.acme.com",
1445 'ping=pong; domain="acme.com"; version=1')
1446 self.assertEquals(len(c), 1)
1447
1448 # illegal domain (host prefix "www.a" contains a dot)
1449 cookie = interact_2965(c, "http://www.a.acme.com",
1450 'whiz=bang; domain="acme.com"; version=1')
1451 self.assertEquals(len(c), 1)
1452
1453 # legal domain
1454 cookie = interact_2965(c, "http://www.a.acme.com",
1455 'wow=flutter; domain=".a.acme.com"; version=1')
1456 self.assertEquals(len(c), 2)
1457
1458 # can't partially match an IP-address
1459 cookie = interact_2965(c, "http://125.125.125.125",
1460 'zzzz=ping; domain="125.125.125"; version=1')
1461 self.assertEquals(len(c), 2)
1462
1463 # illegal path (must be prefix of request path)
1464 cookie = interact_2965(c, "http://www.sol.no",
1465 'blah=rhubarb; domain=".sol.no"; path="/foo"; '
1466 'version=1')
1467 self.assertEquals(len(c), 2)
1468
1469 # legal path
1470 cookie = interact_2965(c, "http://www.sol.no/foo/bar",
1471 'bing=bong; domain=".sol.no"; path="/foo"; '
1472 'version=1')
1473 self.assertEquals(len(c), 3)
1474
1475 # illegal port (request-port not in list)
1476 cookie = interact_2965(c, "http://www.sol.no",
1477 'whiz=ffft; domain=".sol.no"; port="90,100"; '
1478 'version=1')
1479 self.assertEquals(len(c), 3)
1480
1481 # legal port
1482 cookie = interact_2965(
1483 c, "http://www.sol.no",
1484 r'bang=wallop; version=1; domain=".sol.no"; '
1485 r'port="90,100, 80,8080"; '
1486 r'max-age=100; Comment = "Just kidding! (\"|\\\\) "')
1487 self.assertEquals(len(c), 4)
1488
1489 # port attribute without any value (current port)
1490 cookie = interact_2965(c, "http://www.sol.no",
1491 'foo9=bar; version=1; domain=".sol.no"; port; '
1492 'max-age=100;')
1493 self.assertEquals(len(c), 5)
1494
1495 # encoded path
1496 # LWP has this test, but unescaping allowed path characters seems
1497 # like a bad idea, so I think this should fail:
1498## cookie = interact_2965(c, "http://www.sol.no/foo/",
1499## r'foo8=bar; version=1; path="/%66oo"')
1500 # but this is OK, because '<' is not an allowed HTTP URL path
1501 # character:
1502 cookie = interact_2965(c, "http://www.sol.no/<oo/",
1503 r'foo8=bar; version=1; path="/%3coo"')
1504 self.assertEquals(len(c), 6)
1505
1506 # save and restore
1507 filename = test_support.TESTFN
1508
1509 try:
1510 c.save(filename, ignore_discard=True)
1511 old = repr(c)
1512
1513 c = LWPCookieJar(policy=pol)
1514 c.load(filename, ignore_discard=True)
1515 finally:
1516 try: os.unlink(filename)
1517 except OSError: pass
1518
1519 self.assertEquals(old, repr(c))
1520
1521 def test_url_encoding(self):
1522 # Try some URL encodings of the PATHs.
1523 # (the behaviour here has changed from libwww-perl)
1524 from cookielib import CookieJar, DefaultCookiePolicy
1525
1526 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1527 interact_2965(c, "http://www.acme.com/foo%2f%25/%3c%3c%0Anew%E5/%E5",
1528 "foo = bar; version = 1")
1529
1530 cookie = interact_2965(
Brett Cannon0bfac6e2008-07-02 21:52:42 +00001531 c, "http://www.acme.com/foo%2f%25/<<%0anewå/æøå",
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001532 'bar=baz; path="/foo/"; version=1');
1533 version_re = re.compile(r'^\$version=\"?1\"?', re.I)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001534 self.assertTrue("foo=bar" in cookie and version_re.search(cookie))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001535
1536 cookie = interact_2965(
Brett Cannon0bfac6e2008-07-02 21:52:42 +00001537 c, "http://www.acme.com/foo/%25/<<%0anewå/æøå")
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001538 self.assertTrue(not cookie)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001539
1540 # unicode URL doesn't raise exception
1541 cookie = interact_2965(c, u"http://www.acme.com/\xfc")
1542
1543 def test_mozilla(self):
1544 # Save / load Mozilla/Netscape cookie file format.
1545 from cookielib import MozillaCookieJar, DefaultCookiePolicy
1546
1547 year_plus_one = time.localtime()[0] + 1
1548
1549 filename = test_support.TESTFN
1550
1551 c = MozillaCookieJar(filename,
1552 policy=DefaultCookiePolicy(rfc2965=True))
1553 interact_2965(c, "http://www.acme.com/",
1554 "foo1=bar; max-age=100; Version=1")
1555 interact_2965(c, "http://www.acme.com/",
1556 'foo2=bar; port="80"; max-age=100; Discard; Version=1')
1557 interact_2965(c, "http://www.acme.com/", "foo3=bar; secure; Version=1")
1558
1559 expires = "expires=09-Nov-%d 23:12:40 GMT" % (year_plus_one,)
1560 interact_netscape(c, "http://www.foo.com/",
1561 "fooa=bar; %s" % expires)
1562 interact_netscape(c, "http://www.foo.com/",
1563 "foob=bar; Domain=.foo.com; %s" % expires)
1564 interact_netscape(c, "http://www.foo.com/",
1565 "fooc=bar; Domain=www.foo.com; %s" % expires)
1566
1567 def save_and_restore(cj, ignore_discard):
1568 try:
1569 cj.save(ignore_discard=ignore_discard)
1570 new_c = MozillaCookieJar(filename,
1571 DefaultCookiePolicy(rfc2965=True))
1572 new_c.load(ignore_discard=ignore_discard)
1573 finally:
1574 try: os.unlink(filename)
1575 except OSError: pass
1576 return new_c
1577
1578 new_c = save_and_restore(c, True)
1579 self.assertEquals(len(new_c), 6) # none discarded
Ezio Melottiaa980582010-01-23 23:04:36 +00001580 self.assertIn("name='foo1', value='bar'", repr(new_c))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001581
1582 new_c = save_and_restore(c, False)
1583 self.assertEquals(len(new_c), 4) # 2 of them discarded on save
Ezio Melottiaa980582010-01-23 23:04:36 +00001584 self.assertIn("name='foo1', value='bar'", repr(new_c))
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001585
1586 def test_netscape_misc(self):
1587 # Some additional Netscape cookies tests.
1588 from cookielib import CookieJar
1589 from urllib2 import Request
1590
1591 c = CookieJar()
1592 headers = []
1593 req = Request("http://foo.bar.acme.com/foo")
1594
1595 # Netscape allows a host part that contains dots
1596 headers.append("Set-Cookie: Customer=WILE_E_COYOTE; domain=.acme.com")
1597 res = FakeResponse(headers, "http://www.acme.com/foo")
1598 c.extract_cookies(res, req)
1599
1600 # and that the domain is the same as the host without adding a leading
1601 # dot to the domain. Should not quote even if strange chars are used
1602 # in the cookie value.
1603 headers.append("Set-Cookie: PART_NUMBER=3,4; domain=foo.bar.acme.com")
1604 res = FakeResponse(headers, "http://www.acme.com/foo")
1605 c.extract_cookies(res, req)
1606
1607 req = Request("http://foo.bar.acme.com/foo")
1608 c.add_cookie_header(req)
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001609 self.assertTrue(
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001610 "PART_NUMBER=3,4" in req.get_header("Cookie") and
1611 "Customer=WILE_E_COYOTE" in req.get_header("Cookie"))
1612
1613 def test_intranet_domains_2965(self):
1614 # Test handling of local intranet hostnames without a dot.
1615 from cookielib import CookieJar, DefaultCookiePolicy
1616
1617 c = CookieJar(DefaultCookiePolicy(rfc2965=True))
1618 interact_2965(c, "http://example/",
1619 "foo1=bar; PORT; Discard; Version=1;")
1620 cookie = interact_2965(c, "http://example/",
1621 'foo2=bar; domain=".local"; Version=1')
Ezio Melottiaa980582010-01-23 23:04:36 +00001622 self.assertIn("foo1=bar", cookie)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001623
1624 interact_2965(c, "http://example/", 'foo3=bar; Version=1')
1625 cookie = interact_2965(c, "http://example/")
Ezio Melottiaa980582010-01-23 23:04:36 +00001626 self.assertIn("foo2=bar", cookie)
1627 self.assertEqual(len(c), 3)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001628
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)
Ezio Melottiaa980582010-01-23 23:04:36 +00001637 self.assertIn("foo1=bar", cookie)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001638
1639 cookie = interact_netscape(c, "http://example/")
Ezio Melottiaa980582010-01-23 23:04:36 +00001640 self.assertIn("foo2=bar", cookie)
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001641 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
Benjamin Peterson5c8da862009-06-30 22:57:08 +00001715 self.assertTrue(not (
Mark Dickinson3e4caeb2009-02-21 20:27:01 +00001716 # a permanent cookie got lost accidentally
Martin v. Löwis2a6ba902004-05-31 18:22:40 +00001717 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)