Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 93a135d

Browse files
collinandersontimgraham
authored andcommitted
Fixed #26158 -- Rewrote http.parse_cookie() to better match browsers.
1 parent e7e5d9b commit 93a135d

File tree

4 files changed

+70
-17
lines changed

4 files changed

+70
-17
lines changed

django/http/cookie.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,21 @@ def _BaseCookie__set(self, key, real_value, coded_value):
5757

5858

5959
def parse_cookie(cookie):
60-
if cookie == '':
61-
return {}
62-
if not isinstance(cookie, http_cookies.BaseCookie):
63-
try:
64-
c = SimpleCookie()
65-
c.load(cookie)
66-
except http_cookies.CookieError:
67-
# Invalid cookie
68-
return {}
69-
else:
70-
c = cookie
60+
"""
61+
Return a dictionary parsed from a `Cookie:` header string.
62+
"""
7163
cookiedict = {}
72-
for key in c.keys():
73-
cookiedict[key] = c.get(key).value
64+
if six.PY2:
65+
cookie = force_str(cookie)
66+
for chunk in cookie.split(str(';')):
67+
if str('=') in chunk:
68+
key, val = chunk.split(str('='), 1)
69+
else:
70+
# Assume an empty name per
71+
# https://bugzilla.mozilla.org/show_bug.cgi?id=169091
72+
key, val = str(''), chunk
73+
key, val = key.strip(), val.strip()
74+
if key or val:
75+
# unquote using Python's algorithm.
76+
cookiedict[key] = http_cookies._unquote(val)
7477
return cookiedict

docs/releases/1.10.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ Requests and Responses
354354
:attr:`~django.http.HttpRequest.content_params` attributes which are
355355
parsed from the ``CONTENT_TYPE`` header.
356356

357+
* The parser for ``request.COOKIES`` is simplified to better match the behavior
358+
of browsers. ``request.COOKIES`` may now contain cookies that are invalid
359+
according to :rfc:`6265` but are possible to set via ``document.cookie``.
360+
357361
Serialization
358362
~~~~~~~~~~~~~
359363

tests/httpwrappers/tests.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,8 @@ def test_decode(self):
676676
c2 = SimpleCookie()
677677
c2.load(c.output()[12:])
678678
self.assertEqual(c['test'].value, c2['test'].value)
679+
c3 = parse_cookie(c.output()[12:])
680+
self.assertEqual(c['test'].value, c3['test'])
679681

680682
def test_decode_2(self):
681683
"""
@@ -686,6 +688,8 @@ def test_decode_2(self):
686688
c2 = SimpleCookie()
687689
c2.load(c.output()[12:])
688690
self.assertEqual(c['test'].value, c2['test'].value)
691+
c3 = parse_cookie(c.output()[12:])
692+
self.assertEqual(c['test'].value, c3['test'])
689693

690694
def test_nonstandard_keys(self):
691695
"""
@@ -699,6 +703,52 @@ def test_repeated_nonstandard_keys(self):
699703
"""
700704
self.assertIn('good_cookie', parse_cookie('a:=b; a:=c; good_cookie=yes').keys())
701705

706+
def test_python_cookies(self):
707+
"""
708+
Test cases copied from Python's Lib/test/test_http_cookies.py
709+
"""
710+
self.assertEqual(parse_cookie('chips=ahoy; vienna=finger'), {'chips': 'ahoy', 'vienna': 'finger'})
711+
# Here parse_cookie() differs from Python's cookie parsing in that it
712+
# treats all semicolons as delimiters, even within quotes.
713+
self.assertEqual(
714+
parse_cookie('keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"'),
715+
{'keebler': '"E=mc2', 'L': '\\"Loves\\"', 'fudge': '\\012', '': '"'}
716+
)
717+
# Illegal cookies that have an '=' char in an unquoted value.
718+
self.assertEqual(parse_cookie('keebler=E=mc2'), {'keebler': 'E=mc2'})
719+
# Cookies with ':' character in their name.
720+
self.assertEqual(parse_cookie('key:term=value:term'), {'key:term': 'value:term'})
721+
# Cookies with '[' and ']'.
722+
self.assertEqual(parse_cookie('a=b; c=[; d=r; f=h'), {'a': 'b', 'c': '[', 'd': 'r', 'f': 'h'})
723+
724+
def test_cookie_edgecases(self):
725+
# Cookies that RFC6265 allows.
726+
self.assertEqual(parse_cookie('a=b; Domain=example.com'), {'a': 'b', 'Domain': 'example.com'})
727+
# parse_cookie() has historically kept only the last cookie with the
728+
# same name.
729+
self.assertEqual(parse_cookie('a=b; h=i; a=c'), {'a': 'c', 'h': 'i'})
730+
731+
def test_invalid_cookies(self):
732+
"""
733+
Cookie strings that go against RFC6265 but browsers will send if set
734+
via document.cookie.
735+
"""
736+
# Chunks without an equals sign appear as unnamed values per
737+
# https://bugzilla.mozilla.org/show_bug.cgi?id=169091
738+
self.assertIn('django_language', parse_cookie('abc=def; unnamed; django_language=en').keys())
739+
# Even a double quote may be an unamed value.
740+
self.assertEqual(parse_cookie('a=b; "; c=d'), {'a': 'b', '': '"', 'c': 'd'})
741+
# Spaces in names and values, and an equals sign in values.
742+
self.assertEqual(parse_cookie('a b c=d e = f; gh=i'), {'a b c': 'd e = f', 'gh': 'i'})
743+
# More characters the spec forbids.
744+
self.assertEqual(parse_cookie('a b,c<>@:/[]?{}=d " =e,f g'), {'a b,c<>@:/[]?{}': 'd " =e,f g'})
745+
# Unicode characters. The spec only allows ASCII.
746+
self.assertEqual(parse_cookie('saint=André Bessette'), {'saint': force_str('André Bessette')})
747+
# Browsers don't send extra whitespace or semicolons in Cookie headers,
748+
# but parse_cookie() should parse whitespace the same way
749+
# document.cookie parses whitespace.
750+
self.assertEqual(parse_cookie(' = b ; ; = ; c = ; '), {'': 'b', 'c': ''})
751+
702752
def test_httponly_after_load(self):
703753
"""
704754
Test that we can use httponly attribute on cookies that we load

tests/requests/tests.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from django.core.handlers.wsgi import LimitedStream, WSGIRequest
1111
from django.http import (
1212
HttpRequest, HttpResponse, RawPostDataException, UnreadablePostError,
13-
parse_cookie,
1413
)
1514
from django.test import RequestFactory, SimpleTestCase, override_settings
1615
from django.test.client import FakePayload
@@ -183,9 +182,6 @@ def wsgi_str(path_info):
183182
request = WSGIRequest({'PATH_INFO': wsgi_str("/سلام/"), 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')})
184183
self.assertEqual(request.path, "/سلام/")
185184

186-
def test_parse_cookie(self):
187-
self.assertEqual(parse_cookie('invalid@key=true'), {})
188-
189185
def test_httprequest_location(self):
190186
request = HttpRequest()
191187
self.assertEqual(request.build_absolute_uri(location="https://www.example.com/asdf"),

0 commit comments

Comments
 (0)