Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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.



git-svn-id: http://code.djangoproject.com/svn/django/trunk@17795 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 2ca980195653a754a1c4726f5bb253147c6cb329 1 parent 3806122
@PaulMcMillan PaulMcMillan authored
View
8 django/contrib/sessions/backends/cache.py
@@ -16,7 +16,13 @@ def cache_key(self):
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()
View
8 django/contrib/sessions/backends/cached_db.py
@@ -21,7 +21,13 @@ def cache_key(self):
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)
View
30 django/contrib/sessions/tests.py
@@ -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 SessionTestsMixin(object):
# 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 @@ def test_iteritems(self):
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 @@ def test_sessionmanager_save(self):
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,6 +301,14 @@ def test_exists_searches_cache_first(self):
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,6 +350,14 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
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):
@@ -394,6 +413,7 @@ def test_no_httponly_session_cookie(self):
self.assertNotIn('httponly',
str(response.cookies[settings.SESSION_COOKIE_NAME]))
+
class CookieSessionTests(SessionTestsMixin, TestCase):
backend = CookieSession
Please sign in to comment.
Something went wrong with that request. Please try again.