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:
Luke Plant 2011-01-24 20:35:46 +00:00
parent 09a63632c5
commit 42c31f6bf0
5 changed files with 84 additions and 63 deletions

View File

@ -1,7 +1,7 @@
from django.conf import settings
from django.contrib.messages import constants
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.crypto import salted_hmac, constant_time_compare
@ -88,9 +88,9 @@ class CookieStorage(BaseStorage):
unstored_messages = []
encoded_data = self._encode(messages)
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.
cookie = CompatCookie() # create outside the loop
cookie = SimpleCookie() # create outside the loop
def stored_length(val):
return len(cookie.value_encode(val)[1])

View File

@ -21,38 +21,76 @@ except ImportError:
# PendingDeprecationWarning
from cgi import parse_qsl
import Cookie
# httponly support exists in Python 2.6's Cookie library,
# but not in Python 2.4 or 2.5.
import Cookie
if Cookie.Morsel._reserved.has_key('httponly'):
_morsel_supports_httponly = 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
else:
class Morsel(Cookie.Morsel):
def __setitem__(self, K, V):
K = K.lower()
if K == "httponly":
if V:
# The superclass rejects httponly as a key,
# so we jump to the grandparent.
super(Cookie.Morsel, self).__setitem__(K, V)
else:
super(Morsel, self).__setitem__(K, V)
if not _morsel_supports_httponly:
class Morsel(Cookie.Morsel):
def __setitem__(self, K, V):
K = K.lower()
if K == "httponly":
if V:
# The superclass rejects httponly as a key,
# so we jump to the grandparent.
super(Cookie.Morsel, self).__setitem__(K, V)
else:
super(Morsel, self).__setitem__(K, V)
def OutputString(self, attrs=None):
output = super(Morsel, self).OutputString(attrs)
if "httponly" in self:
output += "; httponly"
return output
def OutputString(self, attrs=None):
output = super(Morsel, self).OutputString(attrs)
if "httponly" in self:
output += "; httponly"
return output
class SimpleCookie(Cookie.SimpleCookie):
def __set(self, key, real_value, coded_value):
M = self.get(key, Morsel())
M.set(key, real_value, coded_value)
dict.__setitem__(self, key, M)
if not _morsel_supports_httponly:
def __set(self, key, real_value, coded_value):
M = self.get(key, Morsel())
M.set(key, real_value, coded_value)
dict.__setitem__(self, key, M)
def __setitem__(self, key, value):
rval, cval = self.value_encode(value)
self.__set(key, rval, cval)
def __setitem__(self, key, value):
rval, cval = self.value_encode(value)
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.encoding import smart_str, iri_to_uri, force_unicode
@ -389,40 +427,12 @@ class QueryDict(MultiValueDict):
for v in list_])
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):
if cookie == '':
return {}
if not isinstance(cookie, Cookie.BaseCookie):
try:
c = CompatCookie()
c = SimpleCookie()
c.load(cookie)
except Cookie.CookieError:
# Invalid cookie
@ -460,7 +470,7 @@ class HttpResponse(object):
else:
self._container = [content]
self._is_string = True
self.cookies = CompatCookie()
self.cookies = SimpleCookie()
if status:
self.status_code = status

View File

@ -155,6 +155,9 @@ their deprecation, as per the :ref:`Django deprecation policy
a :class:`~django.contrib.gis.geos.GEOSException` when called
on a geometry with no SRID value.
* :class:`~django.http.CompatCookie` will be removed in favour of
:class:`~django.http.SimpleCookie`.
* 2.0
* ``django.views.defaults.shortcut()``. This function has been moved
to ``django.contrib.contenttypes.views.shortcut()`` as part of the

View File

@ -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
argument could never actually be passed from the Django form machinery it is
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.

View File

@ -1,7 +1,7 @@
import copy
import pickle
from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError
from django.http import QueryDict, HttpResponse, SimpleCookie, BadHeaderError
from django.utils import unittest
class QueryDictTests(unittest.TestCase):
@ -250,7 +250,7 @@ class CookieTests(unittest.TestCase):
# Python 2.4 compatibility note: Python 2.4's cookie implementation
# always returns Set-Cookie headers terminating in semi-colons.
# That's not the bug this test is looking for, so ignore it.
c = CompatCookie()
c = SimpleCookie()
c['test'] = "An,awkward;value"
self.assert_(";" not in c.output().rstrip(';')) # IE 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
"""
c = CompatCookie()
c = SimpleCookie()
c['test'] = "An,awkward;value"
c2 = CompatCookie()
c2 = SimpleCookie()
c2.load(c.output())
self.assertEqual(c['test'].value, c2['test'].value)
@ -269,8 +269,8 @@ class CookieTests(unittest.TestCase):
"""
Test that we haven't broken normal encoding
"""
c = CompatCookie()
c = SimpleCookie()
c['test'] = "\xf0"
c2 = CompatCookie()
c2 = SimpleCookie()
c2.load(c.output())
self.assertEqual(c['test'].value, c2['test'].value)