Loading django/http/cookie.py +16 −13 Original line number Diff line number Diff line Loading @@ -57,18 +57,21 @@ else: def parse_cookie(cookie): if cookie == '': return {} if not isinstance(cookie, http_cookies.BaseCookie): try: c = SimpleCookie() c.load(cookie) except http_cookies.CookieError: # Invalid cookie return {} else: c = cookie """ Return a dictionary parsed from a `Cookie:` header string. """ cookiedict = {} for key in c.keys(): cookiedict[key] = c.get(key).value if six.PY2: cookie = force_str(cookie) for chunk in cookie.split(str(';')): if str('=') in chunk: key, val = chunk.split(str('='), 1) else: # Assume an empty name per # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 key, val = str(''), chunk key, val = key.strip(), val.strip() if key or val: # unquote using Python's algorithm. cookiedict[key] = http_cookies._unquote(val) return cookiedict docs/releases/1.10.txt +4 −0 Original line number Diff line number Diff line Loading @@ -354,6 +354,10 @@ Requests and Responses :attr:`~django.http.HttpRequest.content_params` attributes which are parsed from the ``CONTENT_TYPE`` header. * The parser for ``request.COOKIES`` is simplified to better match the behavior of browsers. ``request.COOKIES`` may now contain cookies that are invalid according to :rfc:`6265` but are possible to set via ``document.cookie``. Serialization ~~~~~~~~~~~~~ Loading tests/httpwrappers/tests.py +50 −0 Original line number Diff line number Diff line Loading @@ -676,6 +676,8 @@ class CookieTests(unittest.TestCase): c2 = SimpleCookie() c2.load(c.output()[12:]) self.assertEqual(c['test'].value, c2['test'].value) c3 = parse_cookie(c.output()[12:]) self.assertEqual(c['test'].value, c3['test']) def test_decode_2(self): """ Loading @@ -686,6 +688,8 @@ class CookieTests(unittest.TestCase): c2 = SimpleCookie() c2.load(c.output()[12:]) self.assertEqual(c['test'].value, c2['test'].value) c3 = parse_cookie(c.output()[12:]) self.assertEqual(c['test'].value, c3['test']) def test_nonstandard_keys(self): """ Loading @@ -699,6 +703,52 @@ class CookieTests(unittest.TestCase): """ self.assertIn('good_cookie', parse_cookie('a:=b; a:=c; good_cookie=yes').keys()) def test_python_cookies(self): """ Test cases copied from Python's Lib/test/test_http_cookies.py """ self.assertEqual(parse_cookie('chips=ahoy; vienna=finger'), {'chips': 'ahoy', 'vienna': 'finger'}) # Here parse_cookie() differs from Python's cookie parsing in that it # treats all semicolons as delimiters, even within quotes. self.assertEqual( parse_cookie('keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"'), {'keebler': '"E=mc2', 'L': '\\"Loves\\"', 'fudge': '\\012', '': '"'} ) # Illegal cookies that have an '=' char in an unquoted value. self.assertEqual(parse_cookie('keebler=E=mc2'), {'keebler': 'E=mc2'}) # Cookies with ':' character in their name. self.assertEqual(parse_cookie('key:term=value:term'), {'key:term': 'value:term'}) # Cookies with '[' and ']'. self.assertEqual(parse_cookie('a=b; c=[; d=r; f=h'), {'a': 'b', 'c': '[', 'd': 'r', 'f': 'h'}) def test_cookie_edgecases(self): # Cookies that RFC6265 allows. self.assertEqual(parse_cookie('a=b; Domain=example.com'), {'a': 'b', 'Domain': 'example.com'}) # parse_cookie() has historically kept only the last cookie with the # same name. self.assertEqual(parse_cookie('a=b; h=i; a=c'), {'a': 'c', 'h': 'i'}) def test_invalid_cookies(self): """ Cookie strings that go against RFC6265 but browsers will send if set via document.cookie. """ # Chunks without an equals sign appear as unnamed values per # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 self.assertIn('django_language', parse_cookie('abc=def; unnamed; django_language=en').keys()) # Even a double quote may be an unamed value. self.assertEqual(parse_cookie('a=b; "; c=d'), {'a': 'b', '': '"', 'c': 'd'}) # Spaces in names and values, and an equals sign in values. self.assertEqual(parse_cookie('a b c=d e = f; gh=i'), {'a b c': 'd e = f', 'gh': 'i'}) # More characters the spec forbids. self.assertEqual(parse_cookie('a b,c<>@:/[]?{}=d " =e,f g'), {'a b,c<>@:/[]?{}': 'd " =e,f g'}) # Unicode characters. The spec only allows ASCII. self.assertEqual(parse_cookie('saint=André Bessette'), {'saint': force_str('André Bessette')}) # Browsers don't send extra whitespace or semicolons in Cookie headers, # but parse_cookie() should parse whitespace the same way # document.cookie parses whitespace. self.assertEqual(parse_cookie(' = b ; ; = ; c = ; '), {'': 'b', 'c': ''}) def test_httponly_after_load(self): """ Test that we can use httponly attribute on cookies that we load Loading tests/requests/tests.py +0 −4 Original line number Diff line number Diff line Loading @@ -10,7 +10,6 @@ from django.core.exceptions import SuspiciousOperation from django.core.handlers.wsgi import LimitedStream, WSGIRequest from django.http import ( HttpRequest, HttpResponse, RawPostDataException, UnreadablePostError, parse_cookie, ) from django.test import RequestFactory, SimpleTestCase, override_settings from django.test.client import FakePayload Loading Loading @@ -183,9 +182,6 @@ class RequestsTests(SimpleTestCase): request = WSGIRequest({'PATH_INFO': wsgi_str("/سلام/"), 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')}) self.assertEqual(request.path, "/سلام/") def test_parse_cookie(self): self.assertEqual(parse_cookie('invalid@key=true'), {}) def test_httprequest_location(self): request = HttpRequest() self.assertEqual(request.build_absolute_uri(location="https://www.example.com/asdf"), Loading Loading
django/http/cookie.py +16 −13 Original line number Diff line number Diff line Loading @@ -57,18 +57,21 @@ else: def parse_cookie(cookie): if cookie == '': return {} if not isinstance(cookie, http_cookies.BaseCookie): try: c = SimpleCookie() c.load(cookie) except http_cookies.CookieError: # Invalid cookie return {} else: c = cookie """ Return a dictionary parsed from a `Cookie:` header string. """ cookiedict = {} for key in c.keys(): cookiedict[key] = c.get(key).value if six.PY2: cookie = force_str(cookie) for chunk in cookie.split(str(';')): if str('=') in chunk: key, val = chunk.split(str('='), 1) else: # Assume an empty name per # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 key, val = str(''), chunk key, val = key.strip(), val.strip() if key or val: # unquote using Python's algorithm. cookiedict[key] = http_cookies._unquote(val) return cookiedict
docs/releases/1.10.txt +4 −0 Original line number Diff line number Diff line Loading @@ -354,6 +354,10 @@ Requests and Responses :attr:`~django.http.HttpRequest.content_params` attributes which are parsed from the ``CONTENT_TYPE`` header. * The parser for ``request.COOKIES`` is simplified to better match the behavior of browsers. ``request.COOKIES`` may now contain cookies that are invalid according to :rfc:`6265` but are possible to set via ``document.cookie``. Serialization ~~~~~~~~~~~~~ Loading
tests/httpwrappers/tests.py +50 −0 Original line number Diff line number Diff line Loading @@ -676,6 +676,8 @@ class CookieTests(unittest.TestCase): c2 = SimpleCookie() c2.load(c.output()[12:]) self.assertEqual(c['test'].value, c2['test'].value) c3 = parse_cookie(c.output()[12:]) self.assertEqual(c['test'].value, c3['test']) def test_decode_2(self): """ Loading @@ -686,6 +688,8 @@ class CookieTests(unittest.TestCase): c2 = SimpleCookie() c2.load(c.output()[12:]) self.assertEqual(c['test'].value, c2['test'].value) c3 = parse_cookie(c.output()[12:]) self.assertEqual(c['test'].value, c3['test']) def test_nonstandard_keys(self): """ Loading @@ -699,6 +703,52 @@ class CookieTests(unittest.TestCase): """ self.assertIn('good_cookie', parse_cookie('a:=b; a:=c; good_cookie=yes').keys()) def test_python_cookies(self): """ Test cases copied from Python's Lib/test/test_http_cookies.py """ self.assertEqual(parse_cookie('chips=ahoy; vienna=finger'), {'chips': 'ahoy', 'vienna': 'finger'}) # Here parse_cookie() differs from Python's cookie parsing in that it # treats all semicolons as delimiters, even within quotes. self.assertEqual( parse_cookie('keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"'), {'keebler': '"E=mc2', 'L': '\\"Loves\\"', 'fudge': '\\012', '': '"'} ) # Illegal cookies that have an '=' char in an unquoted value. self.assertEqual(parse_cookie('keebler=E=mc2'), {'keebler': 'E=mc2'}) # Cookies with ':' character in their name. self.assertEqual(parse_cookie('key:term=value:term'), {'key:term': 'value:term'}) # Cookies with '[' and ']'. self.assertEqual(parse_cookie('a=b; c=[; d=r; f=h'), {'a': 'b', 'c': '[', 'd': 'r', 'f': 'h'}) def test_cookie_edgecases(self): # Cookies that RFC6265 allows. self.assertEqual(parse_cookie('a=b; Domain=example.com'), {'a': 'b', 'Domain': 'example.com'}) # parse_cookie() has historically kept only the last cookie with the # same name. self.assertEqual(parse_cookie('a=b; h=i; a=c'), {'a': 'c', 'h': 'i'}) def test_invalid_cookies(self): """ Cookie strings that go against RFC6265 but browsers will send if set via document.cookie. """ # Chunks without an equals sign appear as unnamed values per # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 self.assertIn('django_language', parse_cookie('abc=def; unnamed; django_language=en').keys()) # Even a double quote may be an unamed value. self.assertEqual(parse_cookie('a=b; "; c=d'), {'a': 'b', '': '"', 'c': 'd'}) # Spaces in names and values, and an equals sign in values. self.assertEqual(parse_cookie('a b c=d e = f; gh=i'), {'a b c': 'd e = f', 'gh': 'i'}) # More characters the spec forbids. self.assertEqual(parse_cookie('a b,c<>@:/[]?{}=d " =e,f g'), {'a b,c<>@:/[]?{}': 'd " =e,f g'}) # Unicode characters. The spec only allows ASCII. self.assertEqual(parse_cookie('saint=André Bessette'), {'saint': force_str('André Bessette')}) # Browsers don't send extra whitespace or semicolons in Cookie headers, # but parse_cookie() should parse whitespace the same way # document.cookie parses whitespace. self.assertEqual(parse_cookie(' = b ; ; = ; c = ; '), {'': 'b', 'c': ''}) def test_httponly_after_load(self): """ Test that we can use httponly attribute on cookies that we load Loading
tests/requests/tests.py +0 −4 Original line number Diff line number Diff line Loading @@ -10,7 +10,6 @@ from django.core.exceptions import SuspiciousOperation from django.core.handlers.wsgi import LimitedStream, WSGIRequest from django.http import ( HttpRequest, HttpResponse, RawPostDataException, UnreadablePostError, parse_cookie, ) from django.test import RequestFactory, SimpleTestCase, override_settings from django.test.client import FakePayload Loading Loading @@ -183,9 +182,6 @@ class RequestsTests(SimpleTestCase): request = WSGIRequest({'PATH_INFO': wsgi_str("/سلام/"), 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')}) self.assertEqual(request.path, "/سلام/") def test_parse_cookie(self): self.assertEqual(parse_cookie('invalid@key=true'), {}) def test_httprequest_location(self): request = HttpRequest() self.assertEqual(request.build_absolute_uri(location="https://www.example.com/asdf"), Loading