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
This commit is contained in:
parent
38061221c3
commit
2ca9801956
|
@ -16,7 +16,13 @@ class SessionStore(SessionBase):
|
||||||
return KEY_PREFIX + self._get_or_create_session_key()
|
return KEY_PREFIX + self._get_or_create_session_key()
|
||||||
|
|
||||||
def load(self):
|
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:
|
if session_data is not None:
|
||||||
return session_data
|
return session_data
|
||||||
self.create()
|
self.create()
|
||||||
|
|
|
@ -21,7 +21,13 @@ class SessionStore(DBStore):
|
||||||
return KEY_PREFIX + self._get_or_create_session_key()
|
return KEY_PREFIX + self._get_or_create_session_key()
|
||||||
|
|
||||||
def load(self):
|
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:
|
if data is None:
|
||||||
data = super(SessionStore, self).load()
|
data = super(SessionStore, self).load()
|
||||||
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
|
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
|
||||||
|
|
|
@ -2,7 +2,9 @@ from __future__ import with_statement
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import shutil
|
import shutil
|
||||||
|
import string
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
|
from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
|
||||||
|
@ -12,10 +14,11 @@ from django.contrib.sessions.backends.file import SessionStore as FileSession
|
||||||
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
|
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
|
||||||
from django.contrib.sessions.models import Session
|
from django.contrib.sessions.models import Session
|
||||||
from django.contrib.sessions.middleware import SessionMiddleware
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
|
from django.core.cache.backends.base import CacheKeyWarning
|
||||||
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
|
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.test import TestCase, RequestFactory
|
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 timezone
|
||||||
from django.utils import unittest
|
from django.utils import unittest
|
||||||
|
|
||||||
|
@ -25,7 +28,7 @@ class SessionTestsMixin(object):
|
||||||
# class, which wouldn't work, and to allow different TestCase subclasses to
|
# class, which wouldn't work, and to allow different TestCase subclasses to
|
||||||
# be used.
|
# be used.
|
||||||
|
|
||||||
backend = None # subclasses must specify
|
backend = None # subclasses must specify
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.session = self.backend()
|
self.session = self.backend()
|
||||||
|
@ -119,13 +122,13 @@ class SessionTestsMixin(object):
|
||||||
self.assertTrue(hasattr(i, '__iter__'))
|
self.assertTrue(hasattr(i, '__iter__'))
|
||||||
self.assertTrue(self.session.accessed)
|
self.assertTrue(self.session.accessed)
|
||||||
self.assertFalse(self.session.modified)
|
self.assertFalse(self.session.modified)
|
||||||
self.assertEqual(list(i), [('x',1)])
|
self.assertEqual(list(i), [('x', 1)])
|
||||||
|
|
||||||
def test_clear(self):
|
def test_clear(self):
|
||||||
self.session['x'] = 1
|
self.session['x'] = 1
|
||||||
self.session.modified = False
|
self.session.modified = False
|
||||||
self.session.accessed = False
|
self.session.accessed = False
|
||||||
self.assertEqual(self.session.items(), [('x',1)])
|
self.assertEqual(self.session.items(), [('x', 1)])
|
||||||
self.session.clear()
|
self.session.clear()
|
||||||
self.assertEqual(self.session.items(), [])
|
self.assertEqual(self.session.items(), [])
|
||||||
self.assertTrue(self.session.accessed)
|
self.assertTrue(self.session.accessed)
|
||||||
|
@ -280,7 +283,7 @@ class DatabaseSessionTests(SessionTestsMixin, TestCase):
|
||||||
|
|
||||||
s = Session.objects.get(session_key=self.session.session_key)
|
s = Session.objects.get(session_key=self.session.session_key)
|
||||||
# Change it
|
# 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
|
# Clear cache, so that it will be retrieved from DB
|
||||||
del self.session._session_cache
|
del self.session._session_cache
|
||||||
self.assertEqual(self.session['y'], 2)
|
self.assertEqual(self.session['y'], 2)
|
||||||
|
@ -298,6 +301,14 @@ class CacheDBSessionTests(SessionTestsMixin, TestCase):
|
||||||
with self.assertNumQueries(0):
|
with self.assertNumQueries(0):
|
||||||
self.assertTrue(self.session.exists(self.session.session_key))
|
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)
|
CacheDBSessionWithTimeZoneTests = override_settings(USE_TZ=True)(CacheDBSessionTests)
|
||||||
|
|
||||||
|
@ -339,6 +350,14 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
|
||||||
|
|
||||||
backend = CacheSession
|
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):
|
class SessionMiddlewareTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -394,6 +413,7 @@ class SessionMiddlewareTests(unittest.TestCase):
|
||||||
self.assertNotIn('httponly',
|
self.assertNotIn('httponly',
|
||||||
str(response.cookies[settings.SESSION_COOKIE_NAME]))
|
str(response.cookies[settings.SESSION_COOKIE_NAME]))
|
||||||
|
|
||||||
|
|
||||||
class CookieSessionTests(SessionTestsMixin, TestCase):
|
class CookieSessionTests(SessionTestsMixin, TestCase):
|
||||||
|
|
||||||
backend = CookieSession
|
backend = CookieSession
|
||||||
|
|
Loading…
Reference in New Issue