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