Author: PaulM
Date: 2012-03-22 22:31:11 -0700 (Thu, 22 Mar 2012)
New Revision: 17795
Modified:
django/trunk/django/contrib/sessions/backends/cache.py
django/trunk/django/contrib/sessions/backends/cached_db.py
django/trunk/django/contrib/sessions/tests.py
Log:
Fixed #17810. Catch session key errors.
Catches memcached session key errors related to overly long session keys.
This is a long-standing bug, but severity was exacerbated by the addition
of cookie-backed session storage, which generates long session values. If
an installation switched from cookie-backed session store to memcached,
users would not be able to log in because of the server error from overly
long memcached keys.
Modified: django/trunk/django/contrib/sessions/backends/cache.py
===================================================================
--- django/trunk/django/contrib/sessions/backends/cache.py 2012-03-23
03:29:30 UTC (rev 17794)
+++ django/trunk/django/contrib/sessions/backends/cache.py 2012-03-23
05:31:11 UTC (rev 17795)
@@ -16,7 +16,13 @@
return KEY_PREFIX + self._get_or_create_session_key()
def load(self):
- session_data = self._cache.get(self.cache_key)
+ try:
+ session_data = self._cache.get(self.cache_key, None)
+ except Exception as e:
+ e_type = str(type(e))
+ if e_type != "<class 'memcache.MemcachedKeyLengthError'>":
+ raise e
+ session_data = None
if session_data is not None:
return session_data
self.create()
Modified: django/trunk/django/contrib/sessions/backends/cached_db.py
===================================================================
--- django/trunk/django/contrib/sessions/backends/cached_db.py 2012-03-23
03:29:30 UTC (rev 17794)
+++ django/trunk/django/contrib/sessions/backends/cached_db.py 2012-03-23
05:31:11 UTC (rev 17795)
@@ -21,7 +21,13 @@
return KEY_PREFIX + self._get_or_create_session_key()
def load(self):
- data = cache.get(self.cache_key, None)
+ try:
+ data = cache.get(self.cache_key, None)
+ except Exception as e:
+ e_type = str(type(e))
+ if e_type != "<class 'memcache.MemcachedKeyLengthError'>":
+ raise e
+ data = None
if data is None:
data = super(SessionStore, self).load()
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
Modified: django/trunk/django/contrib/sessions/tests.py
===================================================================
--- django/trunk/django/contrib/sessions/tests.py 2012-03-23 03:29:30 UTC
(rev 17794)
+++ django/trunk/django/contrib/sessions/tests.py 2012-03-23 05:31:11 UTC
(rev 17795)
@@ -2,7 +2,9 @@
from datetime import datetime, timedelta
import shutil
+import string
import tempfile
+import warnings
from django.conf import settings
from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
@@ -12,10 +14,11 @@
from django.contrib.sessions.backends.signed_cookies import SessionStore as
CookieSession
from django.contrib.sessions.models import Session
from django.contrib.sessions.middleware import SessionMiddleware
+from django.core.cache.backends.base import CacheKeyWarning
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.http import HttpResponse
from django.test import TestCase, RequestFactory
-from django.test.utils import override_settings
+from django.test.utils import override_settings, get_warnings_state,
restore_warnings_state
from django.utils import timezone
from django.utils import unittest
@@ -25,7 +28,7 @@
# class, which wouldn't work, and to allow different TestCase subclasses to
# be used.
- backend = None # subclasses must specify
+ backend = None # subclasses must specify
def setUp(self):
self.session = self.backend()
@@ -119,13 +122,13 @@
self.assertTrue(hasattr(i, '__iter__'))
self.assertTrue(self.session.accessed)
self.assertFalse(self.session.modified)
- self.assertEqual(list(i), [('x',1)])
+ self.assertEqual(list(i), [('x', 1)])
def test_clear(self):
self.session['x'] = 1
self.session.modified = False
self.session.accessed = False
- self.assertEqual(self.session.items(), [('x',1)])
+ self.assertEqual(self.session.items(), [('x', 1)])
self.session.clear()
self.assertEqual(self.session.items(), [])
self.assertTrue(self.session.accessed)
@@ -280,7 +283,7 @@
s = Session.objects.get(session_key=self.session.session_key)
# Change it
- Session.objects.save(s.session_key, {'y':2}, s.expire_date)
+ Session.objects.save(s.session_key, {'y': 2}, s.expire_date)
# Clear cache, so that it will be retrieved from DB
del self.session._session_cache
self.assertEqual(self.session['y'], 2)
@@ -298,7 +301,15 @@
with self.assertNumQueries(0):
self.assertTrue(self.session.exists(self.session.session_key))
+ def test_load_overlong_key(self):
+ warnings_state = get_warnings_state()
+ warnings.filterwarnings('ignore',
+ category=CacheKeyWarning)
+ self.session._session_key = (string.ascii_letters + string.digits) * 20
+ self.assertEqual(self.session.load(), {})
+ restore_warnings_state(warnings_state)
+
CacheDBSessionWithTimeZoneTests =
override_settings(USE_TZ=True)(CacheDBSessionTests)
@@ -339,7 +350,15 @@
backend = CacheSession
+ def test_load_overlong_key(self):
+ warnings_state = get_warnings_state()
+ warnings.filterwarnings('ignore',
+ category=CacheKeyWarning)
+ self.session._session_key = (string.ascii_letters + string.digits) * 20
+ self.assertEqual(self.session.load(), {})
+ restore_warnings_state(warnings_state)
+
class SessionMiddlewareTests(unittest.TestCase):
@override_settings(SESSION_COOKIE_SECURE=True)
@@ -394,6 +413,7 @@
self.assertNotIn('httponly',
str(response.cookies[settings.SESSION_COOKIE_NAME]))
+
class CookieSessionTests(SessionTestsMixin, TestCase):
backend = CookieSession
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en.