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