Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #17476 -- Ensure timezone-dependant cache keys only use ASCII c…

…haracters, especially on Windows.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17286 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 3367913c3db97a15b64f03646ac2c4486a77376f 1 parent ec07a30
@aaugustin aaugustin authored
Showing with 36 additions and 6 deletions.
  1. +7 −4 django/utils/cache.py
  2. +29 −2 tests/regressiontests/cache/tests.py
View
11 django/utils/cache.py
@@ -23,7 +23,7 @@
from django.conf import settings
from django.core.cache import get_cache
-from django.utils.encoding import smart_str, iri_to_uri
+from django.utils.encoding import smart_str, iri_to_uri, force_unicode
from django.utils.http import http_date
from django.utils.timezone import get_current_timezone_name
from django.utils.translation import get_language
@@ -165,9 +165,12 @@ def _i18n_cache_key_suffix(request, cache_key):
# which in turn can also fall back to settings.LANGUAGE_CODE
cache_key += '.%s' % getattr(request, 'LANGUAGE_CODE', get_language())
if settings.USE_TZ:
- # Windows uses non-standard timezone names that may include spaces,
- # which triggers CacheKeyWarning.
- cache_key += '.%s' % get_current_timezone_name().replace(' ', '_')
+ # The datetime module doesn't restrict the output of tzname().
+ # Windows is known to use non-standard, locale-dependant names.
+ # User-defined tzinfo classes may return absolutely anything.
+ # Hence this paranoid conversion to create a valid cache key.
+ tz_name = force_unicode(get_current_timezone_name(), errors='ignore')
+ cache_key += '.%s' % tz_name.encode('ascii', 'ignore').replace(' ', '_')
return cache_key
def _generate_cache_key(request, method, headerlist, key_prefix):
View
31 tests/regressiontests/cache/tests.py
@@ -28,6 +28,7 @@
from django.utils import timezone, translation, unittest
from django.utils.cache import (patch_vary_headers, get_cache_key,
learn_cache_key, patch_cache_control, patch_response_headers)
+from django.utils.encoding import force_unicode
from django.views.decorators.cache import cache_page
from .models import Poll, expensive_calculation
@@ -1270,7 +1271,10 @@ def test_cache_key_i18n_formatting(self):
@override_settings(USE_I18N=False, USE_L10N=False, USE_TZ=True)
def test_cache_key_i18n_timezone(self):
request = self._get_request()
- tz = timezone.get_current_timezone_name().replace(' ', '_')
+ # This is tightly coupled to the implementation,
+ # but it's the most straightforward way to test the key.
+ tz = force_unicode(timezone.get_current_timezone_name(), errors='ignore')
+ tz = tz.encode('ascii', 'ignore').replace(' ', '_')
response = HttpResponse()
key = learn_cache_key(request, response)
self.assertIn(tz, key, "Cache keys should include the time zone name when time zones are active")
@@ -1281,12 +1285,35 @@ def test_cache_key_i18n_timezone(self):
def test_cache_key_no_i18n (self):
request = self._get_request()
lang = translation.get_language()
- tz = timezone.get_current_timezone_name().replace(' ', '_')
+ tz = force_unicode(timezone.get_current_timezone_name(), errors='ignore')
+ tz = tz.encode('ascii', 'ignore').replace(' ', '_')
response = HttpResponse()
key = learn_cache_key(request, response)
self.assertNotIn(lang, key, "Cache keys shouldn't include the language name when i18n isn't active")
self.assertNotIn(tz, key, "Cache keys shouldn't include the time zone name when i18n isn't active")
+ @override_settings(USE_I18N=False, USE_L10N=False, USE_TZ=True)
+ def test_cache_key_with_non_ascii_tzname(self):
+ # Regression test for #17476
+ class CustomTzName(timezone.UTC):
+ name = ''
+ def tzname(self, dt):
+ return self.name
+
+ request = self._get_request()
+ response = HttpResponse()
+ with timezone.override(CustomTzName()):
+ CustomTzName.name = 'Hora estándar de Argentina' # UTF-8 string
+ sanitized_name = 'Hora_estndar_de_Argentina'
+ self.assertIn(sanitized_name, learn_cache_key(request, response),
+ "Cache keys should include the time zone name when time zones are active")
+
+ CustomTzName.name = u'Hora estándar de Argentina' # unicode
+ sanitized_name = 'Hora_estndar_de_Argentina'
+ self.assertIn(sanitized_name, learn_cache_key(request, response),
+ "Cache keys should include the time zone name when time zones are active")
+
+
@override_settings(
CACHE_MIDDLEWARE_KEY_PREFIX="test",
CACHE_MIDDLEWARE_SECONDS=60,
Please sign in to comment.
Something went wrong with that request. Please try again.