Commit 2ca98019 authored by Paul McMillan's avatar Paul McMillan
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
parent 38061221
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -16,7 +16,13 @@ class SessionStore(SessionBase):
        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()
+7 −1
Original line number Diff line number Diff line
@@ -21,7 +21,13 @@ class SessionStore(DBStore):
        return KEY_PREFIX + self._get_or_create_session_key()

    def load(self):
        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)
+25 −5
Original line number Diff line number Diff line
@@ -2,7 +2,9 @@ from __future__ import with_statement

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.file import SessionStore as FileSession
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

@@ -298,6 +301,14 @@ class CacheDBSessionTests(SessionTestsMixin, TestCase):
        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 @@ class SessionMiddlewareTests(unittest.TestCase):
        self.assertNotIn('httponly',
                         str(response.cookies[settings.SESSION_COOKIE_NAME]))


class CookieSessionTests(SessionTestsMixin, TestCase):

    backend = CookieSession