Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
Aymeric Augustin authored December 29, 2011
11  django/utils/cache.py
@@ -23,7 +23,7 @@
23 23
 
24 24
 from django.conf import settings
25 25
 from django.core.cache import get_cache
26  
-from django.utils.encoding import smart_str, iri_to_uri
  26
+from django.utils.encoding import smart_str, iri_to_uri, force_unicode
27 27
 from django.utils.http import http_date
28 28
 from django.utils.timezone import get_current_timezone_name
29 29
 from django.utils.translation import get_language
@@ -165,9 +165,12 @@ def _i18n_cache_key_suffix(request, cache_key):
165 165
         # which in turn can also fall back to settings.LANGUAGE_CODE
166 166
         cache_key += '.%s' % getattr(request, 'LANGUAGE_CODE', get_language())
167 167
     if settings.USE_TZ:
168  
-        # Windows uses non-standard timezone names that may include spaces,
169  
-        # which triggers CacheKeyWarning.
170  
-        cache_key += '.%s' % get_current_timezone_name().replace(' ', '_')
  168
+        # The datetime module doesn't restrict the output of tzname().
  169
+        # Windows is known to use non-standard, locale-dependant names.
  170
+        # User-defined tzinfo classes may return absolutely anything.
  171
+        # Hence this paranoid conversion to create a valid cache key.
  172
+        tz_name = force_unicode(get_current_timezone_name(), errors='ignore')
  173
+        cache_key += '.%s' % tz_name.encode('ascii', 'ignore').replace(' ', '_')
171 174
     return cache_key
172 175
 
173 176
 def _generate_cache_key(request, method, headerlist, key_prefix):
31  tests/regressiontests/cache/tests.py
@@ -28,6 +28,7 @@
28 28
 from django.utils import timezone, translation, unittest
29 29
 from django.utils.cache import (patch_vary_headers, get_cache_key,
30 30
     learn_cache_key, patch_cache_control, patch_response_headers)
  31
+from django.utils.encoding import force_unicode
31 32
 from django.views.decorators.cache import cache_page
32 33
 
33 34
 from .models import Poll, expensive_calculation
@@ -1270,7 +1271,10 @@ def test_cache_key_i18n_formatting(self):
1270 1271
     @override_settings(USE_I18N=False, USE_L10N=False, USE_TZ=True)
1271 1272
     def test_cache_key_i18n_timezone(self):
1272 1273
         request = self._get_request()
1273  
-        tz = timezone.get_current_timezone_name().replace(' ', '_')
  1274
+        # This is tightly coupled to the implementation,
  1275
+        # but it's the most straightforward way to test the key.
  1276
+        tz = force_unicode(timezone.get_current_timezone_name(), errors='ignore')
  1277
+        tz = tz.encode('ascii', 'ignore').replace(' ', '_')
1274 1278
         response = HttpResponse()
1275 1279
         key = learn_cache_key(request, response)
1276 1280
         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):
1281 1285
     def test_cache_key_no_i18n (self):
1282 1286
         request = self._get_request()
1283 1287
         lang = translation.get_language()
1284  
-        tz = timezone.get_current_timezone_name().replace(' ', '_')
  1288
+        tz = force_unicode(timezone.get_current_timezone_name(), errors='ignore')
  1289
+        tz = tz.encode('ascii', 'ignore').replace(' ', '_')
1285 1290
         response = HttpResponse()
1286 1291
         key = learn_cache_key(request, response)
1287 1292
         self.assertNotIn(lang, key, "Cache keys shouldn't include the language name when i18n isn't active")
1288 1293
         self.assertNotIn(tz, key, "Cache keys shouldn't include the time zone name when i18n isn't active")
1289 1294
 
  1295
+    @override_settings(USE_I18N=False, USE_L10N=False, USE_TZ=True)
  1296
+    def test_cache_key_with_non_ascii_tzname(self):
  1297
+        # Regression test for #17476
  1298
+        class CustomTzName(timezone.UTC):
  1299
+            name = ''
  1300
+            def tzname(self, dt):
  1301
+                return self.name
  1302
+
  1303
+        request = self._get_request()
  1304
+        response = HttpResponse()
  1305
+        with timezone.override(CustomTzName()):
  1306
+            CustomTzName.name = 'Hora estándar de Argentina'    # UTF-8 string
  1307
+            sanitized_name = 'Hora_estndar_de_Argentina'
  1308
+            self.assertIn(sanitized_name, learn_cache_key(request, response),
  1309
+                    "Cache keys should include the time zone name when time zones are active")
  1310
+
  1311
+            CustomTzName.name = u'Hora estándar de Argentina'    # unicode
  1312
+            sanitized_name = 'Hora_estndar_de_Argentina'
  1313
+            self.assertIn(sanitized_name, learn_cache_key(request, response),
  1314
+                    "Cache keys should include the time zone name when time zones are active")
  1315
+
  1316
+
1290 1317
     @override_settings(
1291 1318
             CACHE_MIDDLEWARE_KEY_PREFIX="test",
1292 1319
             CACHE_MIDDLEWARE_SECONDS=60,

0 notes on commit 3367913

Please sign in to comment.
Something went wrong with that request. Please try again.