Rationalised CompatCookie/SimpleCookie into single SimpleCookie class with all fixes.
Since upstream Python has fixed the encoding bug (see http://bugs.python.org/issue9824), we don't want a separate class for this bug fix, or several layers for the different fixes. git-svn-id: http://code.djangoproject.com/svn/django/trunk@15298 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
09a63632c5
commit
42c31f6bf0
|
@ -1,7 +1,7 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.messages import constants
|
from django.contrib.messages import constants
|
||||||
from django.contrib.messages.storage.base import BaseStorage, Message
|
from django.contrib.messages.storage.base import BaseStorage, Message
|
||||||
from django.http import CompatCookie
|
from django.http import SimpleCookie
|
||||||
from django.utils import simplejson as json
|
from django.utils import simplejson as json
|
||||||
from django.utils.crypto import salted_hmac, constant_time_compare
|
from django.utils.crypto import salted_hmac, constant_time_compare
|
||||||
|
|
||||||
|
@ -88,9 +88,9 @@ class CookieStorage(BaseStorage):
|
||||||
unstored_messages = []
|
unstored_messages = []
|
||||||
encoded_data = self._encode(messages)
|
encoded_data = self._encode(messages)
|
||||||
if self.max_cookie_size:
|
if self.max_cookie_size:
|
||||||
# data is going to be stored eventually by CompatCookie, which
|
# data is going to be stored eventually by SimpleCookie, which
|
||||||
# adds it's own overhead, which we must account for.
|
# adds it's own overhead, which we must account for.
|
||||||
cookie = CompatCookie() # create outside the loop
|
cookie = SimpleCookie() # create outside the loop
|
||||||
def stored_length(val):
|
def stored_length(val):
|
||||||
return len(cookie.value_encode(val)[1])
|
return len(cookie.value_encode(val)[1])
|
||||||
|
|
||||||
|
|
|
@ -21,38 +21,76 @@ except ImportError:
|
||||||
# PendingDeprecationWarning
|
# PendingDeprecationWarning
|
||||||
from cgi import parse_qsl
|
from cgi import parse_qsl
|
||||||
|
|
||||||
|
import Cookie
|
||||||
# httponly support exists in Python 2.6's Cookie library,
|
# httponly support exists in Python 2.6's Cookie library,
|
||||||
# but not in Python 2.4 or 2.5.
|
# but not in Python 2.4 or 2.5.
|
||||||
import Cookie
|
_morsel_supports_httponly = Cookie.Morsel._reserved.has_key('httponly')
|
||||||
if Cookie.Morsel._reserved.has_key('httponly'):
|
# Some versions of Python 2.7 and later won't need this encoding bug fix:
|
||||||
|
_cookie_encodes_correctly = Cookie.SimpleCookie().value_encode(';') == (';', '"\\073"')
|
||||||
|
|
||||||
|
if _morsel_supports_httponly and _cookie_encodes_correctly:
|
||||||
SimpleCookie = Cookie.SimpleCookie
|
SimpleCookie = Cookie.SimpleCookie
|
||||||
else:
|
else:
|
||||||
class Morsel(Cookie.Morsel):
|
if not _morsel_supports_httponly:
|
||||||
def __setitem__(self, K, V):
|
class Morsel(Cookie.Morsel):
|
||||||
K = K.lower()
|
def __setitem__(self, K, V):
|
||||||
if K == "httponly":
|
K = K.lower()
|
||||||
if V:
|
if K == "httponly":
|
||||||
# The superclass rejects httponly as a key,
|
if V:
|
||||||
# so we jump to the grandparent.
|
# The superclass rejects httponly as a key,
|
||||||
super(Cookie.Morsel, self).__setitem__(K, V)
|
# so we jump to the grandparent.
|
||||||
else:
|
super(Cookie.Morsel, self).__setitem__(K, V)
|
||||||
super(Morsel, self).__setitem__(K, V)
|
else:
|
||||||
|
super(Morsel, self).__setitem__(K, V)
|
||||||
|
|
||||||
def OutputString(self, attrs=None):
|
def OutputString(self, attrs=None):
|
||||||
output = super(Morsel, self).OutputString(attrs)
|
output = super(Morsel, self).OutputString(attrs)
|
||||||
if "httponly" in self:
|
if "httponly" in self:
|
||||||
output += "; httponly"
|
output += "; httponly"
|
||||||
return output
|
return output
|
||||||
|
|
||||||
class SimpleCookie(Cookie.SimpleCookie):
|
class SimpleCookie(Cookie.SimpleCookie):
|
||||||
def __set(self, key, real_value, coded_value):
|
if not _morsel_supports_httponly:
|
||||||
M = self.get(key, Morsel())
|
def __set(self, key, real_value, coded_value):
|
||||||
M.set(key, real_value, coded_value)
|
M = self.get(key, Morsel())
|
||||||
dict.__setitem__(self, key, M)
|
M.set(key, real_value, coded_value)
|
||||||
|
dict.__setitem__(self, key, M)
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
rval, cval = self.value_encode(value)
|
rval, cval = self.value_encode(value)
|
||||||
self.__set(key, rval, cval)
|
self.__set(key, rval, cval)
|
||||||
|
|
||||||
|
if not _cookie_encodes_correctly:
|
||||||
|
def value_encode(self, val):
|
||||||
|
# Some browsers do not support quoted-string from RFC 2109,
|
||||||
|
# including some versions of Safari and Internet Explorer.
|
||||||
|
# These browsers split on ';', and some versions of Safari
|
||||||
|
# are known to split on ', '. Therefore, we encode ';' and ','
|
||||||
|
|
||||||
|
# SimpleCookie already does the hard work of encoding and decoding.
|
||||||
|
# It uses octal sequences like '\\012' for newline etc.
|
||||||
|
# and non-ASCII chars. We just make use of this mechanism, to
|
||||||
|
# avoid introducing two encoding schemes which would be confusing
|
||||||
|
# and especially awkward for javascript.
|
||||||
|
|
||||||
|
# NB, contrary to Python docs, value_encode returns a tuple containing
|
||||||
|
# (real val, encoded_val)
|
||||||
|
val, encoded = super(SimpleCookie, self).value_encode(val)
|
||||||
|
|
||||||
|
encoded = encoded.replace(";", "\\073").replace(",","\\054")
|
||||||
|
# If encoded now contains any quoted chars, we need double quotes
|
||||||
|
# around the whole string.
|
||||||
|
if "\\" in encoded and not encoded.startswith('"'):
|
||||||
|
encoded = '"' + encoded + '"'
|
||||||
|
|
||||||
|
return val, encoded
|
||||||
|
|
||||||
|
class CompatCookie(SimpleCookie):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CompatCookie, self).__init__(*args, **kwargs)
|
||||||
|
import warnings
|
||||||
|
warnings.warn("CompatCookie is deprecated, use django.http.SimpleCookie instead.",
|
||||||
|
PendingDeprecationWarning)
|
||||||
|
|
||||||
from django.utils.datastructures import MultiValueDict, ImmutableList
|
from django.utils.datastructures import MultiValueDict, ImmutableList
|
||||||
from django.utils.encoding import smart_str, iri_to_uri, force_unicode
|
from django.utils.encoding import smart_str, iri_to_uri, force_unicode
|
||||||
|
@ -389,40 +427,12 @@ class QueryDict(MultiValueDict):
|
||||||
for v in list_])
|
for v in list_])
|
||||||
return '&'.join(output)
|
return '&'.join(output)
|
||||||
|
|
||||||
class CompatCookie(SimpleCookie):
|
|
||||||
"""
|
|
||||||
Cookie class that handles some issues with browser compatibility.
|
|
||||||
"""
|
|
||||||
def value_encode(self, val):
|
|
||||||
# Some browsers do not support quoted-string from RFC 2109,
|
|
||||||
# including some versions of Safari and Internet Explorer.
|
|
||||||
# These browsers split on ';', and some versions of Safari
|
|
||||||
# are known to split on ', '. Therefore, we encode ';' and ','
|
|
||||||
|
|
||||||
# SimpleCookie already does the hard work of encoding and decoding.
|
|
||||||
# It uses octal sequences like '\\012' for newline etc.
|
|
||||||
# and non-ASCII chars. We just make use of this mechanism, to
|
|
||||||
# avoid introducing two encoding schemes which would be confusing
|
|
||||||
# and especially awkward for javascript.
|
|
||||||
|
|
||||||
# NB, contrary to Python docs, value_encode returns a tuple containing
|
|
||||||
# (real val, encoded_val)
|
|
||||||
val, encoded = super(CompatCookie, self).value_encode(val)
|
|
||||||
|
|
||||||
encoded = encoded.replace(";", "\\073").replace(",","\\054")
|
|
||||||
# If encoded now contains any quoted chars, we need double quotes
|
|
||||||
# around the whole string.
|
|
||||||
if "\\" in encoded and not encoded.startswith('"'):
|
|
||||||
encoded = '"' + encoded + '"'
|
|
||||||
|
|
||||||
return val, encoded
|
|
||||||
|
|
||||||
def parse_cookie(cookie):
|
def parse_cookie(cookie):
|
||||||
if cookie == '':
|
if cookie == '':
|
||||||
return {}
|
return {}
|
||||||
if not isinstance(cookie, Cookie.BaseCookie):
|
if not isinstance(cookie, Cookie.BaseCookie):
|
||||||
try:
|
try:
|
||||||
c = CompatCookie()
|
c = SimpleCookie()
|
||||||
c.load(cookie)
|
c.load(cookie)
|
||||||
except Cookie.CookieError:
|
except Cookie.CookieError:
|
||||||
# Invalid cookie
|
# Invalid cookie
|
||||||
|
@ -460,7 +470,7 @@ class HttpResponse(object):
|
||||||
else:
|
else:
|
||||||
self._container = [content]
|
self._container = [content]
|
||||||
self._is_string = True
|
self._is_string = True
|
||||||
self.cookies = CompatCookie()
|
self.cookies = SimpleCookie()
|
||||||
if status:
|
if status:
|
||||||
self.status_code = status
|
self.status_code = status
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,9 @@ their deprecation, as per the :ref:`Django deprecation policy
|
||||||
a :class:`~django.contrib.gis.geos.GEOSException` when called
|
a :class:`~django.contrib.gis.geos.GEOSException` when called
|
||||||
on a geometry with no SRID value.
|
on a geometry with no SRID value.
|
||||||
|
|
||||||
|
* :class:`~django.http.CompatCookie` will be removed in favour of
|
||||||
|
:class:`~django.http.SimpleCookie`.
|
||||||
|
|
||||||
* 2.0
|
* 2.0
|
||||||
* ``django.views.defaults.shortcut()``. This function has been moved
|
* ``django.views.defaults.shortcut()``. This function has been moved
|
||||||
to ``django.contrib.contenttypes.views.shortcut()`` as part of the
|
to ``django.contrib.contenttypes.views.shortcut()`` as part of the
|
||||||
|
|
|
@ -609,3 +609,11 @@ Previously this field's ``clean()`` method accepted a second, gender, argument
|
||||||
which allowed stronger validation checks to be made, however since this
|
which allowed stronger validation checks to be made, however since this
|
||||||
argument could never actually be passed from the Django form machinery it is
|
argument could never actually be passed from the Django form machinery it is
|
||||||
now pending deprecation.
|
now pending deprecation.
|
||||||
|
|
||||||
|
``CompatCookie``
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Previously, ``django.http`` exposed an undocumented ``CompatCookie`` class,
|
||||||
|
which was a bug-fix wrapper around the standard library ``SimpleCookie``. As the
|
||||||
|
fixes are moving upstream, this is now deprecated - you should use ``from
|
||||||
|
django.http import SimpleCookie`` instead.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import copy
|
import copy
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError
|
from django.http import QueryDict, HttpResponse, SimpleCookie, BadHeaderError
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
|
|
||||||
class QueryDictTests(unittest.TestCase):
|
class QueryDictTests(unittest.TestCase):
|
||||||
|
@ -250,7 +250,7 @@ class CookieTests(unittest.TestCase):
|
||||||
# Python 2.4 compatibility note: Python 2.4's cookie implementation
|
# Python 2.4 compatibility note: Python 2.4's cookie implementation
|
||||||
# always returns Set-Cookie headers terminating in semi-colons.
|
# always returns Set-Cookie headers terminating in semi-colons.
|
||||||
# That's not the bug this test is looking for, so ignore it.
|
# That's not the bug this test is looking for, so ignore it.
|
||||||
c = CompatCookie()
|
c = SimpleCookie()
|
||||||
c['test'] = "An,awkward;value"
|
c['test'] = "An,awkward;value"
|
||||||
self.assert_(";" not in c.output().rstrip(';')) # IE compat
|
self.assert_(";" not in c.output().rstrip(';')) # IE compat
|
||||||
self.assert_("," not in c.output().rstrip(';')) # Safari compat
|
self.assert_("," not in c.output().rstrip(';')) # Safari compat
|
||||||
|
@ -259,9 +259,9 @@ class CookieTests(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
Test that we can still preserve semi-colons and commas
|
Test that we can still preserve semi-colons and commas
|
||||||
"""
|
"""
|
||||||
c = CompatCookie()
|
c = SimpleCookie()
|
||||||
c['test'] = "An,awkward;value"
|
c['test'] = "An,awkward;value"
|
||||||
c2 = CompatCookie()
|
c2 = SimpleCookie()
|
||||||
c2.load(c.output())
|
c2.load(c.output())
|
||||||
self.assertEqual(c['test'].value, c2['test'].value)
|
self.assertEqual(c['test'].value, c2['test'].value)
|
||||||
|
|
||||||
|
@ -269,8 +269,8 @@ class CookieTests(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
Test that we haven't broken normal encoding
|
Test that we haven't broken normal encoding
|
||||||
"""
|
"""
|
||||||
c = CompatCookie()
|
c = SimpleCookie()
|
||||||
c['test'] = "\xf0"
|
c['test'] = "\xf0"
|
||||||
c2 = CompatCookie()
|
c2 = SimpleCookie()
|
||||||
c2.load(c.output())
|
c2.load(c.output())
|
||||||
self.assertEqual(c['test'].value, c2['test'].value)
|
self.assertEqual(c['test'].value, c2['test'].value)
|
||||||
|
|
Loading…
Reference in New Issue